在最近简要回顾了Haskell之后,对于monad本质上是什么,有什么简单、简洁、实用的解释?
我发现,我遇到的大多数解释都很难理解,而且缺乏实际细节。
在最近简要回顾了Haskell之后,对于monad本质上是什么,有什么简单、简洁、实用的解释?
我发现,我遇到的大多数解释都很难理解,而且缺乏实际细节。
当前回答
另一种尝试是解释monad,只使用Python列表和map函数。我完全接受这不是一个完整的解释,但我希望它能触及核心概念。
我从Monads上的funfunfunction视频和Learn You A Haskell章节“为了几个Monads更多”中得到了这一点的基础。我强烈推荐观看funfunfunction视频。
最简单的是,Monad是具有map和flatMap函数(在Haskell中绑定)的对象。有一些额外的必需财产,但这些是核心属性。
flatMap“展平”map的输出,对于列表,这只是连接列表的值,例如。
concat([[1], [4], [9]]) = [1, 4, 9]
因此,在Python中,我们基本上可以通过以下两个函数实现Monad:
def flatMap(func, lst):
return concat(map(func, lst))
def concat(lst):
return sum(lst, [])
func是任何接受值并返回列表的函数。
lambda x: [x*x]
解释
为了清楚起见,我通过一个简单的函数在Python中创建了concat函数,该函数将列表相加,即[]+[1]+[4]+[9]=[1,4,9](Haskell有一个原生的concat方法)。
我假设你知道地图功能是什么,例如:
>>> list(map(lambda x: [x*x], [1,2,3]))
[[1], [4], [9]]
展平是Monad的关键概念,对于每个作为Monad的对象,这种展平允许您获得Monad中包裹的值。
现在我们可以呼叫:
>>> flatMap(lambda x: [x*x], [1,2,3])
[1, 4, 9]
这个lambda取一个值x并将其放入一个列表中。monad适用于从值到monad类型的任何函数,所以在本例中是列表。
这是你的monad定义。
我认为为什么它们有用的问题已经在其他问题中得到了回答。
更多说明
其他不是列表的例子有JavaScript Promise,它有then方法,JavaScript Streams有flatMap方法。
因此Promise和Streams使用了一个稍微不同的函数,它将Stream或Promise展平,并从内部返回值。
Haskell列表monad具有以下定义:
instance Monad [] where
return x = [x]
xs >>= f = concat (map f xs)
fail _ = []
即有三个函数return(不要与大多数其他语言中的return混淆)、>>=(flatMap)和fail。
希望您能看到以下两者之间的相似之处:
xs >>= f = concat (map f xs)
and:
def flatMap(f, xs):
return concat(map(f, xs))
其他回答
我对monads还是个新手,但我想我会分享一个我觉得读起来很好的链接(带图片!!):http://www.matusiak.eu/numerodix/blog/2012/3/11/monads-for-the-layman/(无隶属关系)
基本上,我从这篇文章中得到的温暖而模糊的概念是monad基本上是适配器,允许不同的函数以可组合的方式工作,即能够将多个函数串起来并混合和匹配它们,而不用担心不一致的返回类型等。因此,当我们尝试制作这些适配器时,BIND函数负责将苹果与苹果、橙子与橙子放在一起。LIFT功能负责使用“较低级别”的功能,并将其“升级”为与BIND功能一起使用并可组合。
我希望我做得对,更重要的是,希望这篇文章对单子有一个有效的观点。如果没有别的话,这篇文章有助于激发我学习更多关于单子叶植物的欲望。
数学思维
简而言之:用于组合计算的代数结构。
返回数据:创建一个只需在monad世界中生成数据的计算。
(return data)>>=(return func):第二个参数接受第一个参数作为数据生成器,并创建连接它们的新计算。
您可以认为(>>=)和return本身不会进行任何计算。他们只是简单地组合和创建计算。
当且仅当main触发时,任何monad计算都将被计算。
解释monad似乎就像解释控制流语句一样。想象一下,一个非程序员要求你解释它们?
你可以给他们一个涉及理论的解释——布尔逻辑、寄存器值、指针、堆栈和框架。但那太疯狂了。
你可以用语法来解释它们。基本上,C中的所有控制流语句都有大括号,您可以通过它们相对于括号的位置来区分条件和条件代码。这可能更疯狂。
或者,您也可以解释循环、if语句、例程、子例程以及可能的协例程。
Monad可以取代相当多的编程技术。语言中有一种特定的语法支持它们,还有一些关于它们的理论。
它们也是函数式程序员使用命令式代码而不承认它的一种方式,但这并不是他们唯一的用途。
monad是一种具有两个操作的数据类型:>>=(又名bind)和return(又名unit)。return接受一个任意值并用它创建monad的实例。>>=接受monad的一个实例并在其上映射一个函数。(您已经可以看到monad是一种奇怪的数据类型,因为在大多数编程语言中,您无法编写一个接受任意值并从中创建类型的函数。monad使用一种参数多态性。)
在Haskell表示法中,monad接口是
class Monad m where
return :: a -> m a
(>>=) :: forall a b . m a -> (a -> m b) -> m b
这些操作应该遵守某些“法则”,但这并不是非常重要的:“法则”只是将操作的合理实现行为化(基本上,>>=和return应该就如何将值转换为monad实例达成一致,并且>>=是关联的)。
Monad不仅仅是关于状态和I/O:它们抽象了一种常见的计算模式,包括处理状态、I/O、异常和非确定性。可能最容易理解的单子是列表和选项类型:
instance Monad [ ] where
[] >>= k = []
(x:xs) >>= k = k x ++ (xs >>= k)
return x = [x]
instance Monad Maybe where
Just x >>= k = k x
Nothing >>= k = Nothing
return x = Just x
其中[]和:是列表构造函数,++是串联运算符,Just和Nothing是Maybe构造函数。这两个monad都在各自的数据类型上封装了常见的有用的计算模式(请注意,两者都与副作用或I/O无关)。
你真的需要写一些非平凡的Haskell代码来理解monad的含义以及它们为什么有用。
Monoid似乎可以确保在Monoid和受支持的类型上定义的所有操作始终返回Monoid内部的受支持类型。任何数字+任何数字=一个数字,没有错误。
而除法接受两个分数,并返回一个分数,该分数在haskell somewhy中将除以零定义为无穷大(恰好是分数somewhy)。。。
在任何情况下,Monads似乎只是一种确保您的操作链以可预测的方式运行的方法,而一个声称为Num->Num的函数,由另一个用x调用的Num->Num的函数组成,并不意味着发射导弹。
另一方面,如果我们有一个功能可以发射导弹,我们可以将它与其他功能组合起来,也可以发射导弹。
在Haskell中,main的类型是IO()或IO[()],这种区分很奇怪,我不会讨论它,但我认为会发生以下情况:
如果我有main,我希望它做一系列动作,我运行程序的原因是产生一个效果——通常是通过IO。因此,我可以将IO操作串联在一起,以便——做IO,而不是其他。
如果我尝试做一些不“返回IO”的事情,程序会抱怨链不流动,或者基本上“这与我们正在尝试做的事情有什么关系——IO动作”,这似乎迫使程序员保持思路,不偏离并思考发射导弹,同时创建排序算法——不流动。
基本上,Monads似乎是编译器的一个提示,“嘿,你知道这个函数在这里返回一个数字,它实际上并不总是有效的,它有时会产生一个number,有时什么都没有,请记住这一点”。知道了这一点,如果你试图断言一个单元动作,单元动作可能会作为一个编译时异常,说“嘿,这实际上不是一个数字,这可能是一个数字。但你不能假设这一点。做一些事情以确保流是可接受的。”这在一定程度上防止了不可预测的程序行为。
似乎monad不是关于纯粹性,也不是关于控制,而是关于维护一个类别的身份,在这个类别上,所有行为都是可预测和定义的,或者不编译。当你被要求做某事时,你不能什么都不做,如果你被要求什么都不干(可见),你也不能做。
我能想到的Monads的最大原因是——看看程序/OOP代码,你会发现你不知道程序从哪里开始,也不知道程序的结束,你看到的只是大量的跳跃和大量的数学、魔法和导弹。您将无法维护它,如果可以的话,您将花费大量的时间来思考整个程序,然后才能理解其中的任何部分,因为在这种情况下,模块化是基于代码的相互依赖的“部分”,其中代码被优化为尽可能相关,以保证效率/相互关系。单子是非常具体的,并且通过定义得到了很好的定义,并确保程序流程可以进行分析,并隔离难以分析的部分——因为它们本身就是单子。monad似乎是一个“可理解的单元,它在完全理解时是可预测的”——如果你理解“可能”monad,那么它除了“可能”之外就没有可能做任何事情,这看起来微不足道,但在大多数非monad代码中,一个简单的函数“helloworld”可以发射导弹,什么都不做,或者摧毁宇宙,甚至扭曲时间——我们不知道也不能保证它是什么样子。一个单子保证它就是什么样子。这是非常强大的。
“现实世界”中的所有事物似乎都是单子,因为它受到防止混淆的明确可观察规律的约束。这并不意味着我们必须模仿这个对象的所有操作来创建类,相反,我们可以简单地说“一个正方形就是一个正方形”,只不过是一个正方形,甚至不是矩形或圆形,和“一个正方形的面积是它现有维度的长度乘以它自身的面积。无论你有什么正方形,如果它是2D空间中的正方形,它的面积绝对不能是任何东西,只有它的长度平方,这几乎是微不足道的。这是非常强大的,因为我们不需要断言我们的世界是这样的,我们只需要使用现实的含义来预测它。”防止我们的节目偏离轨道。
我几乎可以肯定是错的,但我认为这可以帮助一些人,所以希望它能帮助一些人。