列表上的 monad 绑定操作不直观

monad bind operation on a list not intuitive to follow

我发现 Option Monad 易于理解,而 List 则不然。

Some(1) >>= { x=>Some(x+1)}
 Ma -> a -> Mb -> Mb 

如果我从 Some(1) 中提取值,我知道它是 1

但在列表中

 List(3,4,5) flatMap { x=> List(x,-x) }

如果我从列表中提取值,我会得到什么?如何让理解过程直观

OptionMaybe 背后的直觉实际上与 List monad 非常相似。主要区别在于 List 是不确定的 - 我们不知道我们可以获得多少个值,当使用 Option 时它总是成功时为 1,失败时为 0。空列表被认为是失败的。

我觉得这个piece描述得很好:

For lists, monadic binding involves joining together a set of calculations for each value in the list. When used with lists, the signature of >>= becomes:

(>>=) :: [a] -> (a -> [b]) -> [b]

That is, given a list of a's and a function that maps an a onto a list of b's, binding applies this function to each of the a's in the input and returns all of the generated b's concatenated into a list.

还有一个 example 列表实现:

instance Monad [] where  
    return x = [x]  
    xs >>= f = concat (map f xs)  
    fail _ = []

很抱歉将 Haskell 放入 Scala 答案中,但那是我用来理解这些东西的资源。

Scala 的 flatMap 不完全是 Haskell 的绑定 >>=,但非常接近它。那么这一切意味着什么?:

想象一个实际情况,您有一个客户列表 List[Client],您可以将它们绑定到单个订单列表 List[Order],该列表将自动为您展平 flatMap>>=。如果您改用 map,则会得到 List[List[Order]]。在实践中,您将为 >>= 提供函数以供使用,类似于您为 fold 提供函数的方式 - 您决定数据必须如何 generated/aggregated/etc。 bind 为您做的是提供一个通用模式来组合两个 monadic 值,并且对于每种类型的 monads 实现都是唯一的。

您可能更愿意将其视为多个抽象级别(从更一般到更少):

  1. Monad 具有将两个 monadic 值合并为一个的绑定操作:(>>=) :: m a -> (a -> m b) -> m b.

  2. List 作为 monad 实例实现绑定,如下所示:xs >>= f = concat (map f xs) - 将函数映射到所有元素并将结果连接到一个列表中。

  3. 您根据需要(客户 -> 订单示例)为绑定函数提供函数 f 的实现。

一旦您了解了 monad 实例的绑定行为(ListOption),您就可以考虑仅使用它和 "forget" 实际实现。