在最近简要回顾了Haskell之后,对于monad本质上是什么,有什么简单、简洁、实用的解释?
我发现,我遇到的大多数解释都很难理解,而且缺乏实际细节。
在最近简要回顾了Haskell之后,对于monad本质上是什么,有什么简单、简洁、实用的解释?
我发现,我遇到的大多数解释都很难理解,而且缺乏实际细节。
当前回答
你应该首先了解函子是什么。在此之前,先了解高阶函数。
高阶函数只是一个以函数为自变量的函数。
函子是任何类型构造T,其中存在一个高阶函数,称之为map,它将类型为A->b的函数(给定任意两个类型A和b)转换为函数Ta->Tb。该map函数还必须遵守恒等式和复合法则,以便以下表达式对所有p和q返回true(Haskell表示法):
map id = id
map (p . q) = map p . map q
例如,名为List的类型构造函数是一个函子,如果它配备了一个类型为(a->b)->Lista->Listb的函数,该函数遵守上述定律。唯一实际的实施是显而易见的。生成的Lista->Listb函数在给定列表上迭代,为每个元素调用(a->b)函数,并返回结果列表。
monad本质上只是一个函子T,它有两个额外的方法,类型为T(T A)->T A的join和类型为A->T A的unit(有时称为return、fork或pure)。对于Haskell中的列表:
join :: [[a]] -> [a]
pure :: a -> [a]
为什么有用?因为例如,您可以使用返回列表的函数映射列表。Join获取生成的列表列表并将它们连接起来。列表是monad,因为这是可能的。
您可以编写一个函数,先映射,然后连接。此函数称为bind或flatMap,或(>>=)或(=<<)。这通常是Haskell中给出monad实例的方式。
monad必须满足某些定律,即联接必须是关联的。这意味着,如果您的值x类型为[[a]]],那么join(join x)应该等于join(map joinx)。纯必须是联接的标识,这样联接(纯x)==x。
其他回答
事实上,与一般人对蒙得斯的理解相反,他们与国家无关。Monads只是一种包装东西的方法,它提供了对包装好的东西进行操作而不展开的方法。
例如,您可以在Haskell中创建一个类型来包装另一个类型:
data Wrapped a = Wrap a
包装我们定义的东西
return :: a -> Wrapped a
return x = Wrap x
要在不展开的情况下执行操作,假设您有一个函数f::a->b,然后您可以执行此操作来提升该函数以作用于包装的值:
fmap :: (a -> b) -> (Wrapped a -> Wrapped b)
fmap f (Wrap x) = Wrap (f x)
这就是所有需要理解的。然而,事实证明,有一个更通用的函数来执行此提升,即bind:
bind :: (a -> Wrapped b) -> (Wrapped a -> Wrapped b)
bind f (Wrap x) = f x
bind可以比fmap做得更多,但反之亦然。实际上,fmap只能用绑定和返回来定义。因此,在定义monad时。。您给出它的类型(这里是Wrapped a),然后说明它的返回和绑定操作是如何工作的。
很酷的是,这是一个普遍的模式,它会在所有地方弹出,以纯方式封装状态只是其中之一。
有关如何使用monad来引入函数依赖关系,从而控制求值顺序(如Haskell的IO monad中所用)的好文章,请查看IOInside。
至于理解单子,不要太担心。读一些你觉得有趣的东西,如果你不马上理解,也不要担心。那就用Haskell这样的语言潜水吧。修道院就是这样一种东西,在那里,通过练习,理解慢慢地进入你的大脑,有一天你突然意识到你理解了它们。
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的含义以及它们为什么有用。
monad是用于封装状态变化的对象的东西。在不允许您具有可修改状态的语言(例如,Haskell)中最常遇到这种情况。
例如文件I/O。
您将能够使用文件I/O的monad来将不断变化的状态本质与使用monad的代码隔离开来。Monad内部的代码可以有效地忽略Monad外部世界的变化状态,这使您更容易理解程序的整体效果。
http://code.google.com/p/monad-tutorial/正是为了解决这个问题而进行的工作。
monad是一种将共享共同上下文的计算组合在一起的方法。这就像建立一个管道网络。当构建网络时,没有数据流过它。但是当我用“bind”和“return”将所有位拼接在一起后,我调用类似runMyMonad monad数据的东西,数据流过管道。