Haskell Monad - 列表中的 Monad 如何工作?
Haskell Monad - How does Monad on list work?
为了理解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]
它按预期工作:
(>>|) [1,2,3,4] (\x->[(x+1)])
[2,3,4,5]
不过我不确定它是如何工作的。
例如:
[ y | y <- [[1],[2]]]
[[1],[2]]
如何将 (\x->([x+1])
应用于 [1,2,3]
的每个列表元素导致 [2,3,4]
而不是 [[2],[3],[4]]
或者很简单,我的困惑似乎源于不理解这个说法 [ y | x <-xs, y <- f x]
实际上是如何工作的
使用“数学定义”通常比使用 Haskell 标准 class 的方法更容易理解单子。即,
class Applicative' m => Monadd m where
join :: m (m a) -> m a
请注意,您可以根据此实现标准版本,反之亦然:
join mma = mma >>= id
ma >>= f = join (fmap f ma)
对于列表,join
(又名concat
)特别简单:
join :: [[a]] -> [a]
join xss = [x | xs <- xss, x <- xs] -- xss::[[a]], xs::[a]
-- join [[1],[2]] ≡ [1,2]
对于您觉得令人困惑的示例,您可以
[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 以及更多描述列表 monad 的内容。
比较:
(=<<) :: Monad m => (a -> m b) -> m a -> m b
列表
concatMap :: (a -> [b]) -> [a] -> [b]
对于 m = []
.
常规 (>>=)
绑定运算符翻转了参数,但除此之外只是一个中缀 concatMap
.
Or quite simply my confusion seems to stem from not understanding how this statement actually works:
(>>|) xs f = [ y | x <- xs, y <- f x ]
由于列表推导等同于列表的 Monad 实例,这个定义有点作弊。你基本上是在说某物是 Monadd 就像它是 Monad 一样,所以你有两个问题:理解列表推导,以及仍然理解 Monad。
可以对列表理解进行去糖处理以更好地理解:
- Removing syntactic sugar: List comprehension in Haskell
对于您的情况,该语句可以用多种其他方式编写:
使用do-notation:
(>>|) xs f = do x <- xs
y <- f x
return y
使用 (>>=)
运算符脱糖:
(>>|) xs f = xs >>= \x ->
f x >>= \y ->
return y
这可以缩短(每行一个重写):
(>>|) xs f = xs >>= \x -> f x >>= \y -> return y -- eta-reduction
≡ (>>|) xs f = xs >>= \x -> f x >>= return -- monad identity
≡ (>>|) xs f = xs >>= \x -> f x -- eta-reduction
≡ (>>|) xs f = xs >>= f -- prefix operator
≡ (>>|) xs f = (>>=) xs f -- point-free
≡ (>>|) = (>>=)
所以从使用列表推导开始,你并没有真正声明一个新的定义,你只是依赖于现有的定义。如果你愿意,你可以在不依赖现有 Monad 实例或列表推导的情况下定义 instance Monadd []
:
使用concatMap
:
instance Monadd [] where
(>>|) xs f = concatMap f xs
再拼写一下:
instance Monadd [] where
(>>|) xs f = concat (map f xs)
再拼写一下:
instance Monadd [] where
(>>|) [] f = []
(>>|) (x:xs) f = let ys = f x in ys ++ ((>>|) xs f)
Monadd 类型 class 应该类似于 return
。我不确定为什么它不见了。
List comprehensions 就像嵌套循环:
xs >>| foo = [ y | x <- xs, y <- foo x]
-- = for x in xs:
-- for y in (foo x):
-- yield y
因此我们有
[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]
关键步骤已标记(*)
。你可以把它当作什么列表推导的定义是.
一个特例是 foo
函数 returns 一个单例列表,就像你的问题一样。那么它确实等同于映射,因为输入列表中的每个元素都变成了输出列表中的一个(转换)元素。
但是列表理解更强大。输入元素也可以有条件地变成无元素(作为过滤器),或几个元素:
[ a, [a1, a2] ++ concat [ [a1, a2], [ a1, a2,
b, ==> [b1] ++ == [b1], == b1,
c, [] ++ [],
d ] [d1, d2] [d1, d2] ] d1, d2 ]
以上等同于
concat (map foo [a,b,c,d])
=
foo a ++ foo b ++ foo c ++ foo d
适合一些foo
。
concat
是list monad的join
,map
是list monad的fmap
。一般来说,对于任何 monad,
m >>= foo = join (fmap foo m)
Monad的本质是:从每个实体"in"一个"structure",有条件地产生同种结构的新元素,并就地拼接:
[ 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 ]
为了理解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]
它按预期工作:
(>>|) [1,2,3,4] (\x->[(x+1)])
[2,3,4,5]
不过我不确定它是如何工作的。 例如:
[ y | y <- [[1],[2]]]
[[1],[2]]
如何将 (\x->([x+1])
应用于 [1,2,3]
的每个列表元素导致 [2,3,4]
而不是 [[2],[3],[4]]
或者很简单,我的困惑似乎源于不理解这个说法 [ y | x <-xs, y <- f x]
实际上是如何工作的
使用“数学定义”通常比使用 Haskell 标准 class 的方法更容易理解单子。即,
class Applicative' m => Monadd m where
join :: m (m a) -> m a
请注意,您可以根据此实现标准版本,反之亦然:
join mma = mma >>= id
ma >>= f = join (fmap f ma)
对于列表,join
(又名concat
)特别简单:
join :: [[a]] -> [a]
join xss = [x | xs <- xss, x <- xs] -- xss::[[a]], xs::[a]
-- join [[1],[2]] ≡ [1,2]
对于您觉得令人困惑的示例,您可以
[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 以及更多描述列表 monad 的内容。
比较:
(=<<) :: Monad m => (a -> m b) -> m a -> m b
列表concatMap :: (a -> [b]) -> [a] -> [b]
对于m = []
.
常规 (>>=)
绑定运算符翻转了参数,但除此之外只是一个中缀 concatMap
.
Or quite simply my confusion seems to stem from not understanding how this statement actually works:
(>>|) xs f = [ y | x <- xs, y <- f x ]
由于列表推导等同于列表的 Monad 实例,这个定义有点作弊。你基本上是在说某物是 Monadd 就像它是 Monad 一样,所以你有两个问题:理解列表推导,以及仍然理解 Monad。
可以对列表理解进行去糖处理以更好地理解:
- Removing syntactic sugar: List comprehension in Haskell
对于您的情况,该语句可以用多种其他方式编写:
使用do-notation:
(>>|) xs f = do x <- xs y <- f x return y
使用
(>>=)
运算符脱糖:(>>|) xs f = xs >>= \x -> f x >>= \y -> return y
这可以缩短(每行一个重写):
(>>|) xs f = xs >>= \x -> f x >>= \y -> return y -- eta-reduction ≡ (>>|) xs f = xs >>= \x -> f x >>= return -- monad identity ≡ (>>|) xs f = xs >>= \x -> f x -- eta-reduction ≡ (>>|) xs f = xs >>= f -- prefix operator ≡ (>>|) xs f = (>>=) xs f -- point-free ≡ (>>|) = (>>=)
所以从使用列表推导开始,你并没有真正声明一个新的定义,你只是依赖于现有的定义。如果你愿意,你可以在不依赖现有 Monad 实例或列表推导的情况下定义 instance Monadd []
:
使用
concatMap
:instance Monadd [] where (>>|) xs f = concatMap f xs
再拼写一下:
instance Monadd [] where (>>|) xs f = concat (map f xs)
再拼写一下:
instance Monadd [] where (>>|) [] f = [] (>>|) (x:xs) f = let ys = f x in ys ++ ((>>|) xs f)
Monadd 类型 class 应该类似于 return
。我不确定为什么它不见了。
List comprehensions 就像嵌套循环:
xs >>| foo = [ y | x <- xs, y <- foo x]
-- = for x in xs:
-- for y in (foo x):
-- yield y
因此我们有
[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]
关键步骤已标记(*)
。你可以把它当作什么列表推导的定义是.
一个特例是 foo
函数 returns 一个单例列表,就像你的问题一样。那么它确实等同于映射,因为输入列表中的每个元素都变成了输出列表中的一个(转换)元素。
但是列表理解更强大。输入元素也可以有条件地变成无元素(作为过滤器),或几个元素:
[ a, [a1, a2] ++ concat [ [a1, a2], [ a1, a2,
b, ==> [b1] ++ == [b1], == b1,
c, [] ++ [],
d ] [d1, d2] [d1, d2] ] d1, d2 ]
以上等同于
concat (map foo [a,b,c,d])
=
foo a ++ foo b ++ foo c ++ foo d
适合一些foo
。
concat
是list monad的join
,map
是list monad的fmap
。一般来说,对于任何 monad,
m >>= foo = join (fmap foo m)
Monad的本质是:从每个实体"in"一个"structure",有条件地产生同种结构的新元素,并就地拼接:
[ 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 ]