IO monad 的 >>= 和 return 的定义是什么?
在看到 List 和 Maybe monad 是如何定义后,我自然好奇如何 >>=
和 return
操作是为 IO monad 定义的。
After seeing how the List and Maybe monads are defined, I naturally became curious about how
the operations >>=
and return
are defined for the IO monad.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
IO
没有具体的实现;它是一个抽象类型,Haskell 报告未定义确切的实现。事实上,没有什么可以阻止将IO
及其Monad
实例实现为编译器原语的实现,而根本没有 Haskell 实现。基本上,
Monad
用作IO
的接口,它本身无法在纯 Haskell 中实现。这可能就是您现阶段需要了解的全部内容,深入研究实现细节可能只会造成混乱,而不是提供洞察力。也就是说,如果您查看 GHC 的源代码,您会发现它将
IO a
表示为一个类似于State# RealWorld ->; 的函数。 (# State# RealWorld, a #)
(使用拆箱元组 作为返回类型),但这是误导性的;这是一个实现细节,这些 State# RealWorld 值在运行时实际上并不存在。无论在理论上还是在实践中,IO
不是一个状态单子1。相反,GHC 使用不纯原语来实现这些 IO 操作; State# RealWorld“值”只是通过引入从一个语句到下一个语句的数据依赖性来阻止编译器对语句重新排序。
但如果您确实想查看 GHC 的
return
和(>>=)
实现,它们就是:其中
unIO
只是解开来自IO
构造函数内部的函数。需要注意的是,
IO a
表示不纯计算的描述,可以运行该计算来生成a
类型的值。事实上,有一种方法可以从 GHC 的 IO 内部表示中获取值,但这并不意味着这通常成立,或者您可以对所有 monad 执行这样的操作。这纯粹是 GHC 方面的实现细节。1 状态 monad 是一个用于访问和改变一系列计算的状态;它表示为
s ->; (a, s)
(其中s
是状态类型),它看起来与 GHC 用于IO
的类型非常相似,因此会造成混淆。There is no specific implementation for
IO
; it's an abstract type, with the exact implementation left undefined by the Haskell Report. Indeed, there's nothing stopping an implementation implementingIO
and itsMonad
instance as compiler primitives, with no Haskell implementation at all.Basically,
Monad
is used as an interface toIO
, which cannot itself be implemented in pure Haskell. That's probably all you need to know at this stage, and diving into implementation details is likely to just confuse, rather than give insight.That said, if you look at GHC's source code, you'll find that it represents
IO a
as a function looking likeState# RealWorld -> (# State# RealWorld, a #)
(using an unboxed tuple as the return type), but this is misleading; it's an implementation detail, and theseState# RealWorld
values do not actually exist at runtime.IO
is not a state monad,1 in theory or in practice.Instead, GHC uses impure primitives to implement these IO operations; the
State# RealWorld
"values" are only to stop the compiler reordering statements by introducing data dependencies from one statement to the next.But if you really want to see GHC's implementation of
return
and(>>=)
, here they are:where
unIO
simply unwraps the function from inside theIO
constructor.It's important to note that
IO a
represents a description of an impure computation that could be run to produce a value of typea
. The fact that there's a way to get values out of GHC's internal representation ofIO
doesn't mean that this holds in general, or that you can do such a thing for all monads. It's purely an implementation detail on the part of GHC.1 The state monad is a monad used for accessing and mutating a state across a series of computations; it's represented as
s -> (a, s)
(wheres
is the type of state), which looks very similar to the type GHC uses forIO
, thus the confusion.您会感到失望,但
IO
monad 中的>>=
并不是那么有趣。引用 GHC 源代码:这意味着
IO
monad 被声明为State
State#
monad 的实例,< Strike> 及其>>=
在那里定义(并且其实现相当容易猜测)。请参阅 Haskell wiki 上的 IO inside 文章,了解有关 <代码>IO单子。
查看 也很有帮助Haskell 文档,其中每个位置的右侧都有小的“源”链接。
更新:还有另一个令人失望的地方,这就是我的答案,因为我没有注意到
State#
中的“#”。然而,
IO
的行为类似于带有抽象RealWorld
状态的State
monad,正如@ehird 所写,
State#
是编译器的内部,>=
的IO
monad 定义在 GHC.Base 模块:You will be disappointed, but the
>>=
inIO
monad isn't that interesting. To quote the GHC source:That means
IO
monad is declared as the instance ofState
State#
monad,and its.>>=
is defined there (and its implementation is fairly easy to guess)See IO inside article on Haskell wiki for more details about the
IO
monad.It is also helpful to look at the Haskell docs, where every position has small "Source" link on the right.
Update: And there goes another disappointment, which is my answer, because I didn't notice the '#' in
State#
.However
IO
behaves likeState
monad carrying abstractRealWorld
stateAs @ehird wrote
State#
is compiler's internal and>>=
for theIO
monad is defined in GHC.Base module:它们不做任何特殊的事情,只是用于排序操作。如果您用不同的名称来考虑它们,将会有所帮助:
>>= 变为“然后,使用上一个操作的结果”
>>变成“然后”,
return 变成“什么也不做,但是什么都不做的结果是”
这就把这个函数变成:
变成:
到最后,IO 并没有什么神奇之处。它就像 C 语言中的分号一样神奇。
They don't do anything special, and are just there for sequencing actions. It would help if you think of them with different names:
>>= becomes "and then, using the result of the previous action,"
>> becomes "and then,"
return becomes "do nothing, but the result of doing nothing is"
This turns this function:
becomes:
In the end, there's nothing magical about IO. It's exactly as magical as a semicolon is in C.