Haskell中的以下bind(>>=)
代码无法编译:
ghci> [[1]] >>= Just
<interactive>:38:11:
Couldn't match type ‘Maybe’ with ‘[]’
Expected type: [t] -> [[t]]
Actual type: [t] -> Maybe [t]
In the second argument of ‘(>>=)’, namely ‘Just’
In the expression: [[1]] >>= Just
但是,在Scala中,它确实编译并运行:
scala> List( List(1) ).flatMap(x => Some(x) )
res1: List[List[Int]] = List(List(1))
Haskell的>>=
签名是:
>>= :: Monad m => m a -> (a -> m b) -> m b
因此,在[[1]] >>= f
中,f
的类型应为:a -> [b]
。
为什么Scala代码会编译?
答案 0 :(得分:19)
正如@chi解释的那样,Scala的flatMap
比Haskell的>>=
更为通用。 Scala文档的完整签名是:
final def flatMap[B, That](f: (A) ⇒ GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That
这隐含与此特定问题无关,因此我们也可以使用更简单的定义:
final def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): List[B]
只有一个问题,Option
不是GenTraversableOnce
的子类,这里有隐式转换.Scala定义了从Option
到Iterable
的隐式转换, Traversable
的子类,它是GenTraversableOnce
的子类。
implicit def option2Iterable[A](xo: Option[A]): Iterable[A]
隐含是在Option
的伴随对象中定义的。
查看隐含的更简单方法是将Option
分配给Iterable
val
:
scala> val i:Iterable[Int] = Some(1)
i: Iterable[Int] = List(1)
Scala使用一些默认规则,选择List
作为Iterable
的实现。
您可以将TraversableOnce
的不同子类型与monad操作结合起来,这一事实来自implicit class
MonadOps
:
implicit class MonadOps[+A](trav: TraversableOnce[A]) {
def map[B](f: A => B): TraversableOnce[B] = trav.toIterator map f
def flatMap[B](f: A => GenTraversableOnce[B]): TraversableOnce[B] = trav.toIterator flatMap f
def withFilter(p: A => Boolean) = trav.toIterator filter p
def filter(p: A => Boolean): TraversableOnce[A] = withFilter(p)
}
这可以通过上述方法增强每个TraversableOnce
。子类型可以自由地定义更高效的版本,这些将影响隐式定义。这是List
的情况。
答案 1 :(得分:10)
引用List
final def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): List[B]
因此,flatMap
比Haskell的(>>=)
更通用,因为它只需要映射函数f
来生成可遍历的类型,不一定是List
。