我是Haskell的新手,我认为我理解monad及其机制(至少对于列表,州,也许,作家和读者monad),但我想理解为什么他们已经按照他们的方式定义或者为什么他们必须成为他们的样子,以帮助我直觉思考它们。
具体来说,阅读是什么让读者或状态monad需要是函数(即\s -> (a,s)
),而不仅仅是作者monad(即(w,a)
)?
此外,只要不使用MonadPlus
功能,编写器monad可以用作状态monad,其中日志用作状态的字符串表示形式吗?与编写器monad一起使用的monadic函数是否允许查看当前日志并随意修改,还是只允许查看monad的bind函数来查看日志?
另外,为什么Monad使用a -> m b
类型而不是m a -> mb
类型的monadic函数定义?函数从基类型转换为monad包装类型有什么自然意义?
感谢您的回答。
答案 0 :(得分:8)
州和读者monad都取决于我们感兴趣的任何州的价值。 我们需要能够访问它,否则我们怎么能写出像
这样的东西foo = do
i <- get
return $ if i == 1 then 2 else 3
所以我们的状态monad自然看起来像一个状态,做一些东西,产生一个新的东西。
与读者monad一样,我们接受一些状态,魔术,并产生一个输出(但没有新状态,因为它是Reader s a <=> s -> a
。
现在这与作家monad非常不同。在作者中,我们并不关心之前在状态中遇到的问题,我们想要做的就是采用当前状态,并在那里堵塞更多东西。因为我们的状态是幺半群,所以我们有一个保证的开始状态mempty
以及在mappend
中阻塞内容的方法。这意味着我们可以在空的上下文中“运行”每个计算,这样就可以将输出融合在一起并获得相同的结果。
好的,现在有几个问题,所以我会一次去一个
如果作家monad可以访问以前写过的结果,那么它看起来像s -> (s, a)
就是状态monad :)所以是的,无论你怎么办状态monad都可以完成与这个增强的作家。
事实上,StateT
和WriterT
monad都有MonadPlus
个实例,所以我不确定你在这里得到了什么..
由于没有任意函数m a -> a
,我们需要一些方法来展开计算以查看结果ala >>=
,因为return :: a -> m a
使得它变得微不足道另一方面,从普通价值到单价价值更为普遍。否则我们只会有这些无用的IO String
,STM Int
以及我们不能依赖于其余的计算,因为fmap
不会让我们提升更多的副作用
答案 1 :(得分:5)
他们的定义不同,因为他们做了不同的事情。
带读者monad。首先要考虑的含义,而不是它是如何运作的。
读者monad中的计算取决于额外的信息,即读者的“环境”。因此,Reader Env Int
是Int
,取决于环境(Env
类型;如果我使用一个环境评估它,我将获得一个Int
值,如果我用不同的环境评估它我会得到另一个Int
值。如果我没有环境,我就不知道Reader env Int
是什么价值。
现在,如果我给它Int
,那么Env
会给我一个什么样的价值?类型为Env -> Int
的函数!因此,e -> a
为每个e
(a
是monad的类型参数的monad; (->) e
如果你喜欢前缀表示法,那么通用Writer Log Int
。
现在让我们想一想作家monad的意思。写入器monad中的计算产生一个值,但它也产生一个“侧面”的额外值:“log”值。当我们将编写器monad中的一系列monadic计算绑定在一起时,日志值将被组合(如果我们要求日志类型为monoid,那么这可以保证日志值可以与其他知识无关) )。因此,Int
是Log
,其中还附带(Log, Int)
类型的值。
听起来很像一对:(w, a)
。并且这推测为w
是每个a
的monad(其中w
是monad的类型参数)。 mempty
上的monoid约束保证我们可以组合日志值也意味着我们有一个明显的起始值(幺半群的标识元素:s -> (a, s)
),所以我们不需要提供任何东西从作者monad中的值中获取值。
状态monad State S Int
的推理实际上是上述的组合; Int
是S
,它们都取决于S
值(因为读者取决于环境)并且还会生成{{1}}值,其中绑定一个序列状态计算应该导致每个人“看到”前一个状态产生的状态。取决于状态值的值是状态值的函数;如果输出“伴随”一个新的状态值,那么我们需要一对。
答案 2 :(得分:2)
另外,为什么Monad使用
a -> m b
类型而不是m a -> m b
类型的monadic函数定义?函数从基类型转换为monad包装类型有什么自然意义?
(我采用了在m
中b
和mb
之间添加空格的图书馆。
看,这就是它成为monad的原因。如果没有这个,我们已经有了函数a -> b
以及从这些函数到f a -> f b
的链接(此链接称为“仿函数”,并且遵守fmap
的规则)。但是,仿函数只能让你将一个“世界”(一个类别)投射到另一个 - 所以无论第一世界中的法律是什么,它们也都存在于第二世界(例如,如果a + b == c
,那么{ {1}})。 monad为你提供了“世界”之间的桥梁。
此外,您不必根据类型为f a (f +) f b == f c
的函数的行为来定义monad,但monad的一个最小规范会告诉您a -> m b
,>>=
的方式, return
和id
有关。可以使用(.)
,>=>
,return
和id
,或使用(.)
,join
,{{1}来定义monad和return
- 你知道,选择哪个函数并不重要。事实证明,id
方便链接。
答案 3 :(得分:0)
具体来说,阅读是什么让读者或状态monad需要是函数(即
\s -> (a,s)
),而不仅仅是作者monad(即(w,a)
)?
函数只是数据。这恰好恰好是最适合所需语义的类型。
此外,只要不使用MonadPlus功能,编写器monad可以用作状态monad,其中日志用作状态的字符串表示形式吗?
您无法传递非空初始状态,除了附加值之外无法修改它。如果你改变了这个,你得到标准的状态monad(只有字符串除外)。
与编写器monad一起使用的monadic函数是否允许查看当前日志并随意修改,还是只允许查看monad的bind函数来查看日志?
你会注意到Writer monad有一个tell
函数,它实际上将新数据添加到日志中。
另外,为什么Monad使用
a -> m b
类型而不是m a -> m b
类型的monadic函数定义?函数从基类型转换为monad包装类型有什么自然意义?
我认为最好的答案是“因为a -> m b
更有用”。