我读过很多文章来解释什么是单子,如何使用unit
和bind
工作,其中一些直接陷入类别理论,如此抽象(至少对我而言),使眼睛流血,有些人完全忽略了这些,并触及了奇怪的类比。墨西哥卷饼,盒子和什么不。
经过几周的研究和大量的油炸神经元,(我认为)我了解了Monads的工作原理。但是,还有一件事让我无法理解,实际上很少有文章涉及(IO和状态除外):
为什么?
为什么Monad重要?他们为什么如此重要?他们正在解决什么问题?那些问题只能用Monad来解决吗,或者还有其他方法吗?
我读过很多文章来解释什么是单子,如何使用unit
和bind
工作,其中一些直接陷入类别理论,如此抽象(至少对我而言),使眼睛流血,有些人完全忽略了这些,并触及了奇怪的类比。墨西哥卷饼,盒子和什么不。
经过几周的研究和大量的油炸神经元,(我认为)我了解了Monads的工作原理。但是,还有一件事让我无法理解,实际上很少有文章涉及(IO和状态除外):
为什么?
为什么Monad重要?他们为什么如此重要?他们正在解决什么问题?那些问题只能用Monad来解决吗,或者还有其他方法吗?
Answers:
您不需要 monad解决任何问题。他们只是使某些事情变得简单。很多人在解释单子时变得过于抽象和理论化。通常,monad是一种在编程中反复出现的模式。通过识别该模式,我们可以简化代码并避免重新实现某些功能。
对于Haskell而言,从具体的角度来看,monads最容易看到的就是符号。 Haskell和其他语言的列表理解也大量使用了monad。您还可以创建类似Control.Monad的库。
这些都提供了有用的简化,一旦为一个monad实现了它,就可以为所有 monad 自动获得它。这是代码重用对于函数式编程而言比其他范例容易得多的主要原因之一。
IO
是Haskell最著名的monad,但我没有列出单个monad的示例。我正在列出它们支持的总体抽象的示例。例如,我试图弄清楚为什么第一个想到将monad用于IO的人会认为这通常是个好主意。
如果查看特定的monad并查看它们可以解决什么问题,则更容易理解。例如,在Haskell中:
IO:允许IO在类型系统中表示,因此您可以将纯函数与执行IO的函数明确分开。
列表:允许您进行列表理解和节点确定性计算。
也许:null的更好替代方案,并支持与C#的null推算运算符相当的东西。
Parsec:用于编写解析器的便捷DSL。
因此,很容易(我希望)了解单个monad的原理,因为它们都很有用。但是,他们解决的问题也大不相同,从表面上看,它们似乎并没有太多共通之处,只是它们都与链接操作的某些逻辑有关。Monad很有用,因为它们使您可以构建各种工具。
上面的示例可以在没有monad的情况下实现吗?当然,它们可以以即席方式实现,但是在语言中内置了monad可以提供直接的语言支持,例如do
-notation可以与所有monad一起使用。
让人感到困惑的一件事是“流行”功能类似bind
和<*>
是面向实践的。但是,要了解这些概念,首先查看其他功能会更容易。还值得一提的是,与其他关联概念相比,monad表现得有些夸张。因此,我将从仿函数开始。
函子提供功能(以Haskell表示法)fmap :: (Functor f) => (a -> b) -> f a -> f b
。换句话说,您具有f
可以将功能提升到的上下文。您可以想象,几乎所有的东西都是函子。列表,也许,函数,I / O,元组,解析器……每个都代表一个可以在其中出现值的上下文。因此,您可以使用fmap
或其内联变体来编写可在几乎任何上下文中工作的极其通用的功能<$>
。
您还想对上下文做些什么?您可能要结合两个上下文。所以,你可能希望得到的推广zip :: [a] -> [b] -> [(a,b)]
,例如像这样:pair :: (Monoidal f) => f a -> f b -> f (a,b)
。
但是,因为它在实践中甚至更有用,所以Haskell库改为提供Applicative
,它是Functor
和Monoidal
,以及的组合Unit
,它只是添加了您可以实际将值“放入”上下文的内部unit
。
您只需声明关于所使用上下文的三件事,就可以编写极其通用的函数。
Monad
您可以在此之上声明另一件事。我之前没有提到的是,您已经有两种方法来组合两个上下文:您不仅可以将pair
它们组合在一起,还可以将它们堆叠在一起,例如可以有一个列表列表。在I / O上下文中,一个示例是可以从文件读取其他I / O操作的I / O操作,因此您将拥有一个type FilePath -> IO (IO a)
。我们如何摆脱那种堆叠以获得可执行的功能IO a
?这就是Monad
s的join
用武之地,它使我们可以合并两个相同类型的堆叠上下文。解析器,Maybe等bind
也是如此。这只是一种更实用的使用方式join
因此,一元上下文只需要提供四样东西,它就可以与为I / O,解析器,故障等开发的几乎所有机器一起使用。
Monads使您可以表达各种非纯计算以及简化代码
而且,重要的是在不损害纯语言构造和从中获得更简洁的语言的前提下
Maybe
与任何外部无关。