IO 是一个自由的 Monad 吗?

Is IO a Free Monad?

blog posts 和 Mark Seemann 的示例中,我第一次看到自由 monad 作为构建纯代码和 IO 代码之间边界的一种方式。我的基本理解是,一个免费的 monad 可以让你构建一个纯函数的程序(抽象语法树 - AST),然后解释器将其翻译成一系列不纯的过程调用。因此,这个解释器将 AST 的纯操作变成了一系列单子 IO 操作。

我想知道这是否是在复制 Haskell 运行时已经对 IO monad 所做的事情。如果我认为 IO 没什么特别的,而是一个常规 Monad,其绑定函数 >>= 通过 IO 中的所有 monadic 操作对 "Real World" 的状态进行排序,那么此排序本身不提供任何计算(如在优秀的答案 here 中解释了免费的单子)。然后,我可以将所有 IO 操作(如 getLinewriteFile 等)视为自由 IO monad 中的操作,并将 Haskell 运行时视为解释器。 runtime通过一些底层系统调用,C FFI调用之类的方式来解释每一个IO action,这显然是不纯的。

因此,在这个视图中,return IO 操作的函数只是构建 AST,然后由 Haskell 运行时解释。但到此为止,一切都是纯粹的。在这个观点中,函数 a -> IO b 不是不纯的,就像自由 monad 中的操作不是不纯的一样。

这种直觉是正确的吗?如果没有,它的不足在哪里?

您的直觉是正确的:IO 类型的函数确实构建了一个动作树,然后由运行时解释。好吧,至少这是一种有效的看待它的方式(另请参阅 Will Ness 的评论)。

与自由 monad 的区别在于只有一个解释器。你不能选择另一个,也不能实现你自己的。

free monad 的 AST 有两个主要属性:第一,它是组合的;第二,它是可分析的。解释器可以通过匹配其构造函数来分析 AST,并相应地执行解释。

IO monad 共享这些属性中的第一个,但不共享第二个。如果您有一个值 IO String,则无法判断它是通过调用 readLn 还是 pure "foo" 或其他方式创建的。