ton*_*ian 41 monads haskell functional-programming
我学习Haskell的方式我开始掌握monad概念并开始在我的代码中使用已知的monad但是从设计者的角度来看我仍然很难接近monad.在OO中有几个规则,比如"识别名词"对象,注意某种状态和界面......但是我无法找到monad的等效资源.
那么你如何将问题确定为monadic?monadic设计有哪些好的设计模式?当你意识到某些代码会更好地重构为monad时,你的方法是什么?
ehi*_*ird 58
一个有用的经验法则是当您在上下文中看到值时 ; monads可以看作是对"效果"的分层:
通常,您通常应该通过在标准Monad Transformer Library中对monad变换器进行分层来设计monad ,这样您就可以将上述效果组合到一个monad中.它们共同处理您可能想要使用的大多数monad.MTL中还包含一些额外的monad,例如概率和供应 monad.
至于开发一个新定义的类型是否是一个单子,以及它如何表现为一个,您可以通过从上升想到它的直觉Functor到Monad:
(<*>)让您从嵌入式函数及其嵌入式参数转换为嵌入式结果.理解这一点的最简单方法是查看以下类型join:
join :: (Monad m) => m (m a) -> m a
Run Code Online (Sandbox Code Playgroud)
这意味着如果您的嵌入式计算结果是新的嵌入式计算,则可以创建执行该计算结果的计算.所以,你可以用一元效应来创建基于先前计算的值的新计算和传输控制流于计算.
有趣的是,这可能是单独构造事物的一个弱点:Applicative计算结构是静态的(即给定的Applicative计算具有一定的效果结构,不能根据中间值改变),而Monad它是动态的.这可能会限制您可以执行的优化; 例如,应用解析器不如monadic解析器强大(嗯,这不是严格的,但实际上是有效的),但它们可以更好地进行优化.
注意,(>>=)可以定义为
m >>= f = join (fmap f m)
Run Code Online (Sandbox Code Playgroud)
所以单子可以简单地用定义return和join(假设这是一个Functor;所有的单子都是合用的函子,但Haskell的类型类层次可惜的是并不需要此历史原因).
另外一点,你可能不应该过分关注monad,无论他们从被误导的非Haskeller那里得到什么样的嗡嗡声.有许多类型类代表有意义和强大的模式,并不是所有的东西都被最好地表达为monad.Applicative,Monoid,Foldable ......使用哪种抽象完全取决于你的情况.而且,当然,仅仅因为某些东西是monad并不意味着它也不能成为其他东西; 作为一个单子只是一种类型的另一种财产.
所以,你不应该过多考虑"识别monads"; 问题更像是:
dav*_*420 15
按照类型.
如果您发现您具有所有这些类型的书面函数
(a -> b) -> YourType a -> YourType ba -> YourType aYourType (YourType a) -> YourType a或所有这些类型
a -> YourType aYourType a -> (a -> YourType b) -> YourType b然后YourType 可能是一个单子.(我说"可能",因为这些功能也必须遵守monad法则.)
(记住你可以重新排序参数,所以例如YourType a -> (a -> b) -> YourType b只是(a -> b) -> YourType a -> YourType b伪装.)
不要只看单子!如果您具有所有这些类型的功能
YourTypeYourType -> YourType -> YourType并且他们遵守幺半群定律,你有一个幺半群!这也很有价值.同样对于其他类型类,最重要的是Functor.
有monads的效果视图:
一旦你熟悉了这些效果,它很容易构建monad与monad变换器相结合.请注意,组合一些monad需要特别小心(特别是Cont和任何带回溯的monad).
有一点需要注意的是,monad并不多.有一些奇特的,不在标准库中,例如概率monad和Cont monad的变化,如Codensity.但是,除非你正在做的事情的数学其不太可能,你会发明(或发现)新的单子,但是如果你使用的Haskell足够长的时间,你会建立很多单子是通用的标准的不同组合.
编辑 - 另请注意,堆叠monad变换器的顺序会产生不同的monad:
如果将ErrorT(转换器)添加到Writer monad,则会获得此monad Either err (log,a)- 如果没有错误,则只能访问日志.
如果将WriterT(transfomer)添加到Error monad,则会获得此monad (log, Either err a),它始终可以访问日志.