输出是有效计算。因此将它封装成monad是有意义的。但输入是上下文相关的计算。因此,将它封装到一个comonad中会更有意义。
然而,在Haskell中,输入和输出都封装在IO
monad中。为什么?
答案 0 :(得分:18)
comonad有一个extract :: w a -> a
方法,无法(合理地)为IO
实施。
在comonad意义上,输入不是真正的上下文敏感。 comonad的“上下文敏感度”意味着它对数据结构中较大的上下文敏感。例如,列表拉链就像一个列表,在任何给定时刻列表中都有一些关于“我们在哪里”的额外位置信息。 IO
数据结构实际上没有任何实际结构,因此没有任何上下文对它敏感。
Monad
结构允许我们使用IO
操作从>>=
类型内部访问输入,因此事情正常。
另外,请注意术语“有效”和“上下文敏感”是非正式的,因此,完全对于monad和comonads的所有示例都没有意义:是函数monad真的“有效”? (,) e
comonad真的是“上下文敏感”吗?
顺便说一下,开发关于monad和comonad如何工作的直觉的最好方法是通过使用它们获得经验(这也适用于许多其他事情)。不幸的是,用一种简短的短语来表达它们并不是一种很好的方法,例如“有效”或“上下文敏感”,这样可以让你了解它们实际的工作方式。这些短语可以帮助一些,但重要的是要记住它们的局限性。
此外,理解类型类的最佳方法是了解其实例(尝试浏览所有提供的实例并将其弄清楚)。完成后,您可以查看类型类如何连接到所有类型。这将为您提供类型类“含义”的良好直觉。我还应该指出,在经历其实例时,最好在你的脑海中保留可能为类型类提供的任何法律(至少)。
答案 1 :(得分:5)
comonad extract :: w a -> a
的签名意味着我们可以使用纯计算来计算a
,而不会产生任何副作用。
另一方面,当我们想要使用副作用产生一些值时,我们使用IO
。即使是看似仅输入的东西也会产生副作用。例如:通过网络接收数据,从文件读取字节并提升流位置,从数据库读取数据可能会产生副作用,例如打开网络连接,记录访问权限,激活某些触发器等等。因此,comonad isn&#39 ; IO
的适当抽象。