在最近简要回顾了Haskell之后,对于monad本质上是什么,有什么简单、简洁、实用的解释?
我发现,我遇到的大多数解释都很难理解,而且缺乏实际细节。
在最近简要回顾了Haskell之后,对于monad本质上是什么,有什么简单、简洁、实用的解释?
我发现,我遇到的大多数解释都很难理解,而且缺乏实际细节。
当前回答
正如丹尼尔·斯皮瓦克(Daniel Spiewak)所解释的,修道院不是隐喻,而是从一种共同模式中产生的一种实用的抽象。
其他回答
数学思维
简而言之:用于组合计算的代数结构。
返回数据:创建一个只需在monad世界中生成数据的计算。
(return data)>>=(return func):第二个参数接受第一个参数作为数据生成器,并创建连接它们的新计算。
您可以认为(>>=)和return本身不会进行任何计算。他们只是简单地组合和创建计算。
当且仅当main触发时,任何monad计算都将被计算。
实际上,monad是函数组合运算符的一种自定义实现,它考虑了副作用以及不兼容的输入和返回值(用于链接)。
公主对F#计算表达式的解释帮助了我,尽管我仍然不能说我真的理解了。
编辑:这个系列-用javascript解释monad-对我来说是一个“打破平衡”的系列。
http://blog.jcoglan.com/2011/03/05/translation-from-haskell-to-javascript-of-selected-portions-of-the-best-introduction-to-monads-ive-ever-read/http://blog.jcoglan.com/2011/03/06/monad-syntax-for-javascript/http://blog.jcoglan.com/2011/03/11/promises-are-the-monad-of-asynchronous-programming/
我认为理解单子是一件让你毛骨悚然的事。从这个意义上说,尽可能多地阅读“教程”是一个好主意,但通常奇怪的东西(不熟悉的语言或语法)会让你的大脑无法专注于基本内容。
有些事情我很难理解:
基于规则的解释对我来说从未奏效,因为大多数实际示例实际上需要的不仅仅是返回/绑定。此外,称之为规则也无济于事。这更像是“有些东西有共同点,我们把它们称为‘单子’,把共同点称为‘规则’”。Return(a->M<a>)和Bind(M<a>->(a->M<b>)->M<b>)很好,但我永远无法理解Bind如何从M<a>中提取a,以便将其传递给a->M<b>。我不认为我在任何地方读过(也许这对其他人来说都很明显),Return(M<a>->a)的反面必须存在于monad内部,它只是不需要暴露。
Monad是一个可应用的(即,你可以将二进制(因此,“n元”)函数提升到(1),并将纯值注入(2))Functor(即,可以映射到(3)的函数,即提升一元函数到(3”),它还具有展平嵌套数据类型的能力(三个概念中的每一个都遵循其相应的一组规则)。在Haskell中,这种扁平化操作称为join。
此“联接”操作的常规(通用、参数化)类型为:
join :: Monad m => m (m a) -> m a
对于任何monad m(注意,类型中的所有ms都是相同的!)。
特定的m monad定义了其特定版本的join,该版本适用于由类型m A的monadic值“携带”的任何值类型A。某些特定类型包括:
join :: [[a]] -> [a] -- for lists, or nondeterministic values
join :: Maybe (Maybe a) -> Maybe a -- for Maybe, or optional values
join :: IO (IO a) -> IO a -- for I/O-produced values
连接操作将产生a型值的m计算的m计算转换为a型值组合的m计算。这允许将计算步骤组合成一个更大的计算。
结合“bind”(>>=)运算符的计算步骤简单地使用fmap和join,即。
(ma >>= k) == join (fmap k ma)
{-
ma :: m a -- `m`-computation which produces `a`-type values
k :: a -> m b -- create new `m`-computation from an `a`-type value
fmap k ma :: m ( m b ) -- `m`-computation of `m`-computation of `b`-type values
(m >>= k) :: m b -- `m`-computation which produces `b`-type values
-}
相反,可以通过bind定义join,join mma==join(fmap id mma)==mma>>=id,其中id ma=ma——对于给定的类型m,以更方便的为准。
对于monad,do表示法及其使用代码的等效绑定,
do { x <- mx ; y <- my ; return (f x y) } -- x :: a , mx :: m a
-- y :: b , my :: m b
mx >>= (\x -> -- nested
my >>= (\y -> -- lambda
return (f x y) )) -- functions
可以读为
首先“做”mx,当它完成时,将其“结果”作为x,让我用它“做”其他事情。
在给定的do块中,绑定箭头<-右侧的每个值对于某些类型a都是m a类型,在整个do块中都是相同的monad m。
返回x是一个中立的m计算,它只产生给定的纯值x,因此将任何m计算与返回绑定都不会改变该计算。
(1) 提升A2::适用m=>(a->b->c)->m a->m b->m c
(2) 纯::适用m=>a->m a
(3) 具有fmap::函数m=>(a->b)->m a->m b
还有等效的Monad方法,
liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
return :: Monad m => a -> m a
liftM :: Monad m => (a -> b) -> m a -> m b
给定monad,其他定义可以如下
pure a = return a
fmap f ma = do { a <- ma ; return (f a) }
liftA2 f ma mb = do { a <- ma ; b <- mb ; return (f a b) }
(ma >>= k) = do { a <- ma ; b <- k a ; return b }
我将尝试在Haskell的背景下解释Monad。
在函数式编程中,函数组合很重要。它允许我们的程序由小的、易于阅读的函数组成。
假设我们有两个函数:g::Int->String和f::String->Bool。
我们可以做(f.g)x,这与f(gx)相同,其中x是Int值。
当进行合成/将一个函数的结果应用到另一个函数时,使类型匹配是很重要的。在上述情况下,g返回的结果类型必须与f接受的类型相同。
但有时值是在上下文中的,这使得排列类型有点不容易。(在上下文中设置值非常有用。例如,Maybe Int类型表示可能不存在的Int值,IO String类型表示由于执行某些副作用而存在的String值。)
假设我们现在有g1::Int->Maybe String和f1::String->Maybe Bool。g1和f1分别与g和f非常相似。
我们不能做(f1.g1)x或f1(g1 x),其中x是Int值。g1返回的结果类型不是f1期望的类型。
我们可以用。运算符,但现在我们不能用..组合f1和g1。。问题是我们不能直接将上下文中的值传递给期望值不在上下文中的函数。
如果我们引入一个运算符来组合g1和f1,这样我们就可以写出(f1 operator g1)x,这不是很好吗?g1返回上下文中的值。该值将脱离上下文并应用于f1。是的,我们有这样一个操作员。它是<=<。
我们还有一个>>=运算符,它为我们做了完全相同的事情,尽管语法略有不同。
我们写:g1 x>>=f1。g1 x是Maybe Int值。>>=运算符帮助将Int值从“可能不存在”上下文中取出,并将其应用于f1。f1的结果是Maybe Bool,它将是整个>>=操作的结果。
最后,为什么Monad有用?因为Monad是定义>>=运算符的类型类,与定义==和/=运算符的Eq类型类非常相似。
总之,Monad类型类定义了>>=运算符,该运算符允许我们将上下文中的值(我们称为这些monadic值)传递给不需要上下文中值的函数。将考虑上下文。
如果这里需要记住一点,那就是Monads允许在上下文中包含值的函数组合。