在看到List和Maybe monad的定义之后,我自然会对如何做好准备
为IO monad定义了操作>>=
和return
。
答案 0 :(得分:44)
IO
没有具体的实施方式;它是一个抽象类型,确切的实现未被Haskell报告定义。实际上,没有什么能阻止实现IO
及其Monad
实例的实现作为编译器原语,而根本没有Haskell实现。
基本上,Monad
用作{{1>}的接口,它本身不能在纯Haskell中实现。这可能是您在此阶段需要了解的全部内容,而深入了解实施细节可能会让您感到困惑,而不是提供见解。
那就是说,如果你看一下GHC的源代码,你会发现它代表IO
的函数看起来像IO a
(使用unboxed tuple作为返回类型),但这是误导;它是一个实现细节,这些State# RealWorld -> (# State# RealWorld, a #)
值在运行时实际上并不存在。在理论上或实践中,State# RealWorld
不是状态monad, 1 。
相反,GHC使用不纯的原语来实现这些IO操作; IO
“values”只是通过将数据依赖从一个语句引入下一个语句来停止编译器重新排序语句。
但是如果你真的想看到GHC的State# RealWorld
和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 m s of (# new_s, a #) -> unIO (k a) new_s
只是从unIO
构造函数中解包该函数。
值得注意的是,IO
表示不纯计算的描述,可以运行该计算以生成IO a
类型的值。事实上有一种方法可以从GHC的内部表示a
中获取值,这并不意味着这一点一般,或者你可以为所有monad做这样的事情。这纯粹是GHC的实施细节。
1 state monad是一个monad,用于通过一系列计算访问和改变状态;它表示为IO
(其中s -> (a, s)
是状态的类型),它看起来与GHC用于s
的类型非常相似,因此存在混淆。
答案 1 :(得分:13)
你会感到失望,但>>=
monad中的IO
并不那么有趣。引用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 inside文章。
查看Haskell docs也很有帮助,其中每个位置右侧都有小的“源”链接。
更新:还有另一个失望,这是我的回答,因为我没有注意到State#
中的'#'。
但是,IO
的行为类似State
monad,其中包含抽象的RealWorld
州
正如@ehird写的那样State#
是编译器的内部,>>=
monad的IO
是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 m s of (# new_s, a #) -> unIO (k a) new_s
答案 2 :(得分:1)
他们没有做任何特别的事情,只是为了排序行动。如果你用不同的名字来思考它们会有所帮助:
>> =变为“然后,使用上一个操作的结果”
>>成为“然后,”
返回变为“什么都不做,但无所事事的结果是”
这会改变这个功能:
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中的分号一样神奇。