[英]Comparing Haskell and Scala Bind/Flatmap Examples
The following bind(>>=)
code, in Haskell, does not compile: 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
But, in Scala, it does actually compile and run: 但是,在Scala中,它确实编译并运行:
scala> List( List(1) ).flatMap(x => Some(x) )
res1: List[List[Int]] = List(List(1))
Haskell's >>=
signature is: Haskell的>>=
签名是:
>>= :: Monad m => ma -> (a -> mb) -> mb
So, in [[1]] >>= f
, f
's type should be: a -> [b]
. 所以,在[[1]] >>= f
, f
的类型应该是: a -> [b]
。
Why does the Scala code compile? 为什么Scala代码会编译?
As @chi explained Scala's flatMap
is more general than the Haskell's >>=
. 正如@chi解释的那样,Scala的flatMap
比Haskell的>>=
更通用。 The full signature from the Scala docs is: Scala文档的完整签名是:
final def flatMap[B, That](f: (A) ⇒ GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That
This implicit isn't relevant for this specific problem, so we could as well use the simpler definition: 这隐含与此特定问题无关,因此我们也可以使用更简单的定义:
final def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): List[B]
There is only one Problem, Option
is no subclass of GenTraversableOnce
, here an implicit conversion comes in. Scala defines an implicit conversion from Option
to Iterable
which is a subclass of Traversable
which is a subclass of GenTraversableOnce
. 只有一个问题, Option
不是GenTraversableOnce
子类,这里有一个隐式转换GenTraversableOnce
定义了从Option
到Iterable
的隐式转换,它是Traversable
的子类,它是GenTraversableOnce
的子类。
implicit def option2Iterable[A](xo: Option[A]): Iterable[A]
The implicit is defined in the companion object of Option
. 隐式在Option
的伴随对象中定义。
A simpler way to see the implicit at work is to assign a Option
to an Iterable
val
: 查看隐式工作的更简单方法是为Iterable
val
分配一个Option
:
scala> val i:Iterable[Int] = Some(1)
i: Iterable[Int] = List(1)
Scala uses some defaulting rules, to select List
as the implementation of Iterable
. Scala使用一些默认规则,选择List
作为Iterable
的实现。
The fact that you can combine different subtypes of TraversableOnce
with monad operations comes from the implicit class
MonadOps
: 您可以将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)
}
This enhances every TraversableOnce
with the methods above. 这会使用上述方法增强每个TraversableOnce
。 The subtypes are free to define more efficient versions on there own, these will shadow the implicit definitions. 子类型可以自由地定义更高效的版本,这些将影响隐式定义。 This is the case for List
. List
就是这种情况。
Quoting from the Scala reference for List
引用List
的Scala引用
final def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): List[B]
So, flatMap
is more general than Haskell's (>>=)
, since it only requires the mapped function f
to generate a traversable type, not necessarily a List
. 因此, flatMap
比Haskell (>>=)
更通用,因为它只需要映射函数f
来生成可遍历类型,不一定是List
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.