IO = monad的定义是什么?
在看到List和Maybe monads是如何定义的之后,我自然而然地开始对IO monad的操作>>=
和return
是如何定义的。
IO
没有具体的实现; 这是一个抽象types,Haskell报告没有定义确切的实现。 事实上,没有什么能够停止实现IO
和Monad
实例作为编译器原语的实现,根本没有Haskell的实现。
基本上, Monad
被用作IO
的接口 ,它本身不能在纯粹的Haskell中实现。 这可能是你在这个阶段需要知道的所有东西,并且潜在的实现细节可能只是混淆,而不是提供洞察力。
这就是说,如果你看GHC的源代码,你会发现它代表IO a
一个函数,看起来像State# RealWorld -> (# State# RealWorld, a #)
(使用一个未装箱的元组作为返回types),但这是误导性的; 这是一个实现细节,而这些State# RealWorld
值实际上并不存在于运行时。 IO
在理论上或实践上不是一个国家的单子。
相反,GHC使用不纯的原语来实现这些IO操作; State# RealWorld
“values”只是通过从一个语句引入数据依赖关系来停止编译器重新sorting语句。
但是如果你真的想看GHC的return
和(>>=)
,这里是:
returnIO :: a -> IO a returnIO x = IO $ \ s -> (# s, x #) bindIO :: IO a -> (a -> IO b) -> IO b bindIO (IO m) k = IO $ \ s -> case ms of (# new_s, a #) -> unIO (ka) new_s
unIO
简单地从IO
构造函数中解开函数。
需要注意的是, IO a
表示可以运行以产生typesa
的值的不纯计算的描述 。 事实上,有一种方法可以从GHC的IO
内部表示中获得价值,这并不意味着这一般性地成立,也不意味着你可以为所有的单子做这样的事情。 这纯粹是GHC部分的实现细节。
1 状态monad是通过一系列计算来访问和变异状态的monad, 它被表示为s -> (a, s)
(其中s
是状态的types),看起来与GHC用于IO
的types非常相似,因此是混淆。
你会失望的,但是IO
>>=
monad中的>>=
并不是那么有趣。 引用GHC来源:
{- | A value of type @'IO' a@ is a computation which, when performed, does some I\/O before returning a value of type @a@. There is really only one way to \"perform\" an I\/O action: bind it to @Main.main@ in your program. When your program is run, the I\/O will be performed. It isn't possible to perform I\/O from an arbitrary function, unless that function is itself in the 'IO' monad and called at some point, directly or indirectly, from @Main.main@. 'IO' is a monad, so 'IO' actions can be combined using either the do-notation or the '>>' and '>>=' operations from the 'Monad' class. -} newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))
这意味着IO
monad被声明为State
State#
monad的实例, 它的 。 >>=
在那里被定义(并且它的实现很容易猜到)
有关IO
monad的更多详细信息,请参阅Haskell wiki上的IO文章。 查看Haskell文档也很有帮助,每个位置右侧都有一个小的“Source”链接。
更新:还有一个失望,那就是我的回答,因为我没有注意到State#
。 然而, IO
行为就像携带抽象的RealWorld
状态的State
monad
正如@ehird所写的, State#
是编译器的内部,而>>=
的IO
monad是在GHC.Base模块中定义的:
instance Monad IO where {-# INLINE return #-} {-# INLINE (>>) #-} {-# INLINE (>>=) #-} m >> k = m >>= \ _ -> k return = returnIO (>>=) = bindIO fail s = failIO s returnIO :: a -> IO a returnIO x = IO $ \ s -> (# s, x #) bindIO :: IO a -> (a -> IO b) -> IO b bindIO (IO m) k = IO $ \ s -> case ms of (# new_s, a #) -> unIO (ka) new_s
他们没有做任何特别的事情,只是在那里进行sorting的行动。 如果你用不同的名字想到它们会有所帮助:
>> =变成“然后,使用前一个动作的结果”
>>变成“然后”
回报变成“什么也不做,但是无所事事的结果是”
这转动这个function:
main :: IO () main = putStr "hello" >> return " world" >>= putStrLn
变为:
main :: IO () main = putStr "hello" and then, do nothing, but the result of doing nothing is " world" and then, using the result of the previous action, putStrLn
最后,IO没有什么神奇的。 它和C中的分号一样神奇