你如何识别monadicdevise模式?
我学习Haskell的方式我已经开始掌握monad概念,并开始使用我的代码中已知的monad,但是从devise者的angular度来看,我仍然遇到困难。 在面向对象中有几个规则,比如“为名称标识名词”,注意某种状态和接口……但是我无法为monadfind相应的资源。
那么如何将自然界中的一个问题看成一元问题呢? 一元devise有什么好的devise模式? 当你意识到某些代码可以更好地重构为monad时,你的方法是什么?
有用的经验法则是当你在上下文中看到值; monads可以被看作是分层的“效果”:
- 也许:偏袒(使用:可能失败的计算)
- 或者:短路错误(用途:错误/exception处理)
- [](单子列表):不确定性(用途:列表生成,过滤,…)
- 状态:单个可变参考(使用:状态)
- 读者:共享环境(使用:variables绑定,通用信息…)
- Writer: “侧通道”输出或累积(使用:logging,维护只写计数器,…)
- Cont:非本地控制stream(用途:太多列出)
通常情况下,您通常应该通过在标准的Monad变压器库中对monad变压器进行分层来devisemonad,从而使您可以将上述效果组合到一个monad中。 一起,这些处理你可能想要使用的大部分单子。 还有一些没有包括在MTL中的单子,如概率和供应单子。
就直觉来说,新定义的types是否是monad,以及它如何performance为一个,你可以从Functor
到Monad
:
- Functor让你用纯函数转换值。
- Applicative允许您embedded纯粹的值并expression应用程序 –
(<*>)
可让您从embedded式函数及其embedded式参数转到embedded式结果。 - Monad让embedded式计算的结构依赖于以前的计算值 。
理解这个最简单的方法是查看join
的types:
join :: (Monad m) => m (ma) -> ma
这意味着如果你有一个embedded式计算,其结果是一个新的embedded式计算,你可以创build一个计算来执行计算结果。 因此,您可以使用monadic效果根据以前的计算值创build新的计算,并将控制stream转移到该计算。
有趣的是,这可能是单调构造的一个弱点 :用Applicative
,计算的结构是静态的(即给定的Applicative
计算具有一定的结构效应,不能根据中间值进行改变),而Monad
则是dynamic的。 这可以限制你可以做的优化; 例如,实用的parsing器比单一的parsing器不那么强大(嗯,这不是严格的 ,但它是有效的),但它们可以更好地优化。
请注意(>>=)
可以定义为
m >>= f = join (fmap fm)
所以monad可以简单地用return
和join
来定义(假设它是一个Functor
;所有monad都是可应用的函子,但是Haskell的typeclass层次结构由于历史原因不幸不需要)。
另外要注意的是,不pipe他们从误导性的非Haskellers那里得到什么样的嗡嗡声,你都不应该把注意力放在monad上。 代表有意义和强大的模式的types有很多,并不是所有的东西都最好用monad来表示。 应用 , Monoid , 可折叠 …使用抽象完全取决于你的情况。 当然,只是因为某个事物是一个单子,并不意味着它不能成为其他事物。 作为一个monad只是另一种types的属性。
所以,你不应该过多地考虑“识别单子”。 问题更像是:
- 这个代码可以用一个更简单的monadicforms来表示吗? 用哪个monad?
- 这种types我刚刚定义了一个monad? monads上的标准函数编码的哪些通用模式可以利用?
按照types。
如果你发现你已经写了所有这些types的函数
-
(a -> b) -> YourType a -> YourType b
-
a -> YourType a
-
YourType (YourType a) -> YourType a
或所有这些types
-
a -> YourType a
-
YourType a -> (a -> YourType b) -> YourType b
那么YourType
可能是一个monad。 (我说“可以”,因为函数也必须服从单子法则。)
(记住你可以对参数进行重新sorting,例如YourType a -> (a -> b) -> YourType b
只是(a -> b) -> YourType a -> YourType b
的伪装。
不要只注意monad! 如果你有所有这些types的function
-
YourType
-
YourType -> YourType -> YourType
他们服从幺半律,你有一个幺半群! 这也可以是有价值的。 类似的其他types,最重要的是Functor。
有monad的效果视图:
- 也许 – 偏好/失败短路
- 要么 – 错误报告/短路(如可能与更多的信息)
- 作家 – 只写“状态”,一般logging
- 读者 – 只读状态,通常环境传递
- 状态 – 读/写状态
- 恢复 – 可计算的计算
- 列表 – 多个成功
一旦你熟悉了这些效果,就很容易将它们与单体变压器相结合。 请注意,组合一些monad需要特别的照顾(特别是Cont和任何带有回溯的monad)。
有一件重要的事情要注意,没有太多单子。 有一些外来的不在标准库中,例如概率monad和Cont monad的变体,如Codensity。 但是除非你正在做一些math上的事情,否则你将发明(或发现)一个新的monad,但是如果你使用Haskell足够长的时间,你将会build立许多不同的标准monad组合。
编辑 – 另外请注意,您堆叠单子变压器的顺序导致不同的单子:
如果将ErrorT(转换器)添加到Writer monad中,则会得到此monad Either err (log,a)
– 如果没有错误,则只能访问日志。
如果将WriterT(transfomer)添加到Error monad中,则会得到此monad (log, Either err a)
,它总是可以访问日志。
这是一个无法回答的问题,但我认为反正说很重要。 请问! StackOverflow,/ r / haskell和#haskell irc频道都是获得智能人士快速反馈的好地方。 如果你正在解决一个问题,而且你怀疑有一些简单的魔法可以让它变得简单,那就问一下! Haskell社区喜欢解决问题,并且是可笑的友好的。
不要误会,我不鼓励你永远不要为自己学习。 恰恰相反,与Haskell社区交互是最好的学习方式之一。 LYAH和RWH ,2款免费在线的Haskell书籍也受到强烈推荐。
哦,别忘了玩,玩,玩! 当你使用monadic代码时,你会开始感受到“形状”monad有什么样的感觉,以及monadic combinators是否有用。 如果你正在推出自己的monad,那么通常types系统会引导你一个明显的,简单的解决scheme。 但说实话,你应该很less需要推出你自己的Monad实例,因为Haskell库提供了其他答案者所提到的大量有用的东西。