简体   繁体   English

Haskell Monad - 列表中的 Monad 如何工作?

[英]Haskell Monad - How does Monad on list work?

In order to understand Monad, I came up with the following definitions:为了理解 Monad,我想出了以下定义:

class Applicative' f where
 purea :: a -> f a
 app :: f (a->b) -> f a -> f b

class Applicative' m =>  Monadd m where
 (>>|) :: m a -> (a -> m b) -> m b

instance Applicative' [] where
 purea x = [x]
 app gs xs = [g x | g <- gs, x <- xs]

instance Monadd [] where
 (>>|) xs f = [ y | x <-xs, y <- f x]

It works as expected:它按预期工作:

(>>|) [1,2,3,4] (\x->[(x+1)])
[2,3,4,5]

I am not sure how it is working though.我不确定它是如何工作的。 For example:例如:

[ y | y <- [[1],[2]]]
[[1],[2]]

How does application (\\x->([x+1]) to each list element of [1,2,3] result in [2,3,4] and not [[2],[3],[4]]应用(\\x->([x+1])[1,2,3]每个列表元素如何导致[2,3,4]而不是[[2],[3],[4]]

Or quite simply my confusion seems to stem from not understanding how this statement [ y | x <-xs, y <- fx]或者很简单,我的困惑似乎源于不理解这个陈述[ y | x <-xs, y <- fx] [ y | x <-xs, y <- fx] actually works [ y | x <-xs, y <- fx]实际上有效

Monads are often easier understood with the “mathematical definition”, than with the methods of the Haskell standard class.使用“数学定义”通常比使用 Haskell 标准类的方法更容易理解 Monad。 Namely,即,

class Applicative' m => Monadd m where
  join :: m (m a) -> m a

Note that you can implement the standard version in terms of this, vice versa:请注意,您可以在此方面实现标准版本,反之亦然:

join mma = mma >>= id

ma >>= f = join (fmap f ma)

For lists, join (aka concat ) is particularly simple:对于列表, join (又名concat )特别简单:

join :: [[a]] -> [a]
join xss = [x | xs <- xss, x <- xs]  -- xss::[[a]], xs::[a]
-- join [[1],[2]] ≡ [1,2]

For the example you find confusing, you'd have对于你觉得令人困惑的例子,你有

[1,2,3,4] >>= \x->[(x+1)]
  ≡   join $ fmap (\x->[(x+1)]) [1,2,3,4]
  ≡   join [[1+1], [2+1], [3+1], [4+1]]
  ≡   join [[2],[3],[4],[5]]
  ≡   [2,3,4,5]

Wadler , School of Haskell , LYAH , HaskellWiki , Quora and many more describe the list monad. WadlerHaskell 学院LYAHHaskellWikiQuora等都描述了列表 monad。

Compare:相比:

The regular (>>=) bind operator has the arguments flipped, but is otherwise just an infix concatMap .常规(>>=)绑定运算符翻转了参数,但在其他方面只是一个中缀concatMap

Or quite simply my confusion seems to stem from not understanding how this statement actually works:或者很简单,我的困惑似乎源于不了解此语句的实际工作原理:

 (>>|) xs f = [ y | x <- xs, y <- fx ]

Since list comprehensions are equivalent to the Monad instance for lists, this definition is kind of cheating.由于列表推导式等同于列表的 Monad 实例,因此这个定义有点像作弊。 You're basically saying that something is a Monadd in the way that it's a Monad, so you're left with two problems: Understanding list comprehensions, and still understanding Monad.您基本上是在说某事物是 Monadd 的方式与它是 Monad 的方式一样,因此您会遇到两个问题:理解列表推导式,以及仍然理解 Monad。

List comprehensions can be de-sugared for a better understanding:列表推导式可以去除糖分以便更好地理解:

In your case, the statement could be written in a number of other ways:在您的情况下,该语句可以用多种其他方式编写:

  • Using do-notation:使用 do-notation:

     (>>|) xs f = do x <- xs y <- fx return y
  • De-sugared into using the (>>=) operator:去糖化为使用(>>=)运算符:

     (>>|) xs f = xs >>= \\x -> fx >>= \\y -> return y
  • This can be shortened (one rewrite per line):这可以缩短(每行重写一次):

     (>>|) xs f = xs >>= \\x -> fx >>= \\y -> return y -- eta-reduction ≡ (>>|) xs f = xs >>= \\x -> fx >>= return -- monad identity ≡ (>>|) xs f = xs >>= \\x -> fx -- eta-reduction ≡ (>>|) xs f = xs >>= f -- prefix operator ≡ (>>|) xs f = (>>=) xs f -- point-free ≡ (>>|) = (>>=)

So from using list comprehensions, you haven't really declared a new definition, you're just relying on the existing one.因此,通过使用列表推导式,您并没有真正声明一个新定义,您只是依赖于现有定义。 If you wanted, you could instead define your instance Monadd [] without relying on existing Monad instances or list comprehensions:如果你愿意,你可以定义你的instance Monadd []而不依赖现有的 Monad 实例或列表instance Monadd []

  • Using concatMap :使用concatMap

     instance Monadd [] where (>>|) xs f = concatMap f xs
  • Spelling that out a little more:再详细说明一下:

     instance Monadd [] where (>>|) xs f = concat (map f xs)
  • Spelling that out even more:更详细地说:

     instance Monadd [] where (>>|) [] f = [] (>>|) (x:xs) f = let ys = fx in ys ++ ((>>|) xs f)

The Monadd type class should have something similar to return . Monadd 类型类应该有类似于return东西。 I'm not sure why it's missing.我不确定为什么它不见了。

List comprehensions are just like nested loops: 列表推导式就像嵌套循环:

   xs >>| foo = [ y | x <- xs, y <- foo x]

--            =   for x in xs:
--                         for y in (foo x):
--                               yield y

Thus we have因此我们有

[1,2,3,4] >>| (\x -> [x, x+10])
=
[ y | x <- [1,2,3,4], y <- (\x -> [x, x+10]) x]
=
[ y | x <- [1] ++ [2,3,4], y <- [x, x+10]]
=
[ y | x <- [1], y <- [x, x+10]] ++ [ y | x <- [2,3,4], y <- [x, x+10]]  -- (*)
=
[ y |           y <- [1, 1+10]]   ++ [ y | x <- [2,3,4], y <- [x, x+10]]
=
[ y | y <- [1]] ++ [ y | y <- [11]] ++ [ y | x <- [2,3,4], y <- [x, x+10]]
=
[1] ++ [11] ++ [ y | x <- [2,3,4], y <- [x, x+10]]
=
[1, 11] ++ [2, 12] ++ [ y | x <- [3,4], y <- [x, x+10]]
=
[1, 11] ++ [2, 12] ++ [3, 13] ++ [ y | x <- [4], y <- [x, x+10]]
=
[1, 11] ++ [2, 12] ++ [3, 13] ++ [4, 14]

The crucial step is marked (*) .关键步骤标记为(*) You can take it as the definition of what list comprehensions are .你可以把它作为什么列表内涵定义。

A special case is when the foo function returns a singleton list, like in your question.一个特殊情况是当foo函数返回一个单例列表时,就像你的问题一样。 Then it is indeed tantamount to mapping , as each element in the input list is turned into one (transformed) element in the output list.那么它确实等同于mapping ,因为输入列表中的每个元素都变成了输出列表中的一个(转换)元素。

But list comprehensions are more powerful.但是列表推导式更强大。 An input element can also be turned conditionally into no elements (working as a filter ), or several elements:输入元素也可以有条件地转换为无元素(用作过滤器)或多个元素:

  [ a,          [a1, a2] ++        concat [ [a1, a2],         [  a1, a2,
    b,    ==>   [b1]     ++    ==           [b1],        ==      b1,
    c,          []       ++                 [],
    d ]         [d1, d2]                    [d1, d2] ]           d1, d2  ]

The above is equivalent to以上等价于

    concat (map foo [a,b,c,d]) 
    =  
    foo a ++ foo b ++ foo c ++ foo d

for some appropriate foo .对于一些合适的foo

concat is list monad's join , and map is list monad's fmap . concat是 list monad 的joinmap是 list monad 的fmap In general, for any monad,一般来说,对于任何 monad,

    m >>= foo  =  join (fmap foo m)

The essence of Monad is: from each entity "in" a "structure", conditionally producing new elements in the same kind of structure, and splicing them in-place: Monad的本质是:从“结构”中的每个实体,有条件地在同一种结构中产生新的元素,并将它们就地拼接:

[     a     ,  b   ,  c  ,    d      ]
    /   \      |      |     /   \
[  [a1, a2] , [b1] ,  [] , [d1, d2]  ]  -- fmap foo    = [foo x | x <- xs]
                                        --             =     [y | x <- xs, y <- [foo x]]
[   a1, a2  ,  b1  ,        d1, d2   ]  -- join (fmap foo) = [y | x <- xs, y <-  foo x ]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM