简体   繁体   English

比较Haskell和Scala绑定/平面图示例

[英]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]] >>= ff的类型应该是: 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定义了从OptionIterable的隐式转换,它是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.

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