Ulr*_*ter 7 monads haskell purely-functional monad-transformers
在阅读 Haskell 教科书中关于不同 monad 的章节时,当作者从解释bind和 monad 定律的细节到实际使用 monad时,我反复迷路。突然,诸如“在 monadic 上下文中运行函数”或“运行 monad”之类的表达出现了。同样,在库文档和关于 monad 转换器堆栈的讨论中,我读到一些函数“可以在任何选择的 monad 中运行”的声明。这个“在 monad 中运行”到底是什么意思?
有两件事我似乎不太明白:
return
, >>=
) 和定律的类型类。因此,在 monad 中“运行”某些东西可能意味着 (a) 将其作为参数提供给return
,或 (b) 使用>>=
. 如果 monad 是 type m a
,那么在 a) 的情况下,某些东西必须是 type a
,以匹配return
函数的类型。如果 b)某事必须是 type 的函数a -> m b
,以匹配函数的类型>>=
。由此,我不明白如何在任意 monad 中“运行”某个函数,因为我排序使用的函数>>=
都必须具有相同的类型签名,并且我使用的值return
必须是特定的 monad 类型参数。run
功能,如runReader
,runState
等,这些功能都没有单子的定义的一部分,他们是普通的功能,而不是在语言的功能核心之外的任何方式的特殊命令语句。那么,他们“跑”什么呢?我觉得清楚地理解这些概念是理解 monad 转换器堆栈或类似结构的关键,这些结构似乎是理解 Haskell 中任何重要库和任何非平凡程序所必需的。非常感谢你帮助我从简单地编写函数式代码到真正理解它的含义。
写书和文章的作者在试图解释概念时经常使用隐喻和不太精确的语言。目的是让读者对正在发生的事情有一个概念上的直觉。
我相信“运行”函数的概念属于这一类。除此之外IO
,你认为你可以使用函数来编写权,发言权,[]
,Maybe
,等没有其他特殊的功能。
我认为,在 monad 内部运行某些东西的概念来自于对函子是容器的观察。这个观察也适用于 monad,因为所有 monad 都是函子。[Bool]
是布尔值Maybe Int
的容器,是(零或一)数字的容器。您甚至可以将 reader 函子r -> a
视为a
值的容器,因为您可以想象它只是一个非常大的查找表。
能够“在容器内运行函数”很有用,因为并非所有容器都可以访问其内容。再一次,IO
是最好的例子,因为它是一个不透明的容器。
一个常见问题是:如何从不纯的方法返回纯值。同样,许多初学者问:我如何获得 a 的值Maybe
?你甚至可以问:我如何从列表中获取值?概括地说,问题变成了:如何从 monad 中获取值。
答案是你没有。您“在容器内运行函数”,或者,如我所愿,您将行为注入 monad。你永远不会离开容器,而是让你的函数在它的上下文中执行。特别是当涉及到 时IO
,这是您可以与该容器交互的唯一方式,因为它在其他方面是不透明的(我在这里假装它unsafePerformIO
不存在)。
请记住,当涉及到bind方法 ( >>=
) 时,虽然“在其内部运行”的函数具有 type a -> m b
,但您也可以a -> b
在 monad 中“运行”一个“普通”函数with fmap
,因为所有Monad
实例也是Functor
实例。