你如何识别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为一个,你可以从FunctorMonad

  • 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可以简单地用returnjoin来定义(假设它是一个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库提供了其他答案者所提到的大量有用的东西。