比较Haskell和Scala绑定/平面地图示例 [英] Comparing Haskell and Scala Bind/Flatmap Examples
问题描述
下面的 bind(>> =)
代码在Haskell中不能编译:
ghci> [[1]]>> =只是
<互动>:38:11:
无法将类型'Maybe'与'[]'
匹配期望类型:[t ] - > [[t]]
实际类型:[t] - >也许[t]
在'(> =)'的第二个参数中,即'Just'
在表达式中:[[1]]>> = Just
但是,在Scala中,它实际上是编译和运行的:
< pre class =lang-scala prettyprint-override>
scala> List(List(1)).flatMap(x => Some(x))
res1:List [List [Int]] = List(List(1))
Haskell的>> =
签名是:
>> = :: Monad m => m a - > (a - > m b) - > mb
因此,在 [[1]]>> = f
, f
的类型应该是: a - > [b]
。
为什么要编译Scala代码?
flatMap
比Haskell的>> =
。 Scala文档的完整签名是:$ b $ pre $
final def flatMap [B,That](f:(A)⇒GenTraversableOnce [ (b):隐式bf:CanBuildFrom [List [A],B,That]):那
这个隐含的内容与这个特定的问题无关,所以我们可以使用更简单的定义:
final def flatMap [B ](f:(A)⇒GenTraversableOnce [B]):List [B]
一个问题, Option
不是 GenTraversableOnce
的子类,这里有一个隐式转换。Scala定义了一个隐式转换, code>选项到 Iterable
它是 Traversable
的一个子类,它是一个 GenTraversableOnce
。
隐式def option2Iterable [A](xo:选项[A]):Iterable [A]
隐式定义在< c $ c> Option 。
一个简单的wa y看到在工作中隐含的是将一个 Option
赋给一个 Iterable
val
:
scala> val I:Iterable [Int] = Some(1)
i:Iterable [Int] = List(1)
Scala使用一些默认规则来选择 List 作为
Iterable
的实现。
您可以将 TraversableOnce
的不同子类型与monad操作相结合,这个事实来自隐式类 code>
MonadOps
:
隐式类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
。
的情况
The following bind(>>=)
code, in Haskell, does not compile:
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> List( List(1) ).flatMap(x => Some(x) )
res1: List[List[Int]] = List(List(1))
Haskell's >>=
signature is:
>>= :: Monad m => m a -> (a -> m b) -> m b
So, in [[1]] >>= f
, f
's type should be: a -> [b]
.
Why does the Scala code compile?
As @chi explained Scala's flatMap
is more general than the Haskell's >>=
. The full signature from the Scala docs is:
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
.
implicit def option2Iterable[A](xo: Option[A]): Iterable[A]
The implicit is defined in the companion object of Option
.
A simpler way to see the implicit at work is to assign a Option
to an Iterable
val
:
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
.
The fact that you can combine different subtypes of TraversableOnce
with monad operations comes from the 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. The subtypes are free to define more efficient versions on there own, these will shadow the implicit definitions. This is the case for List
.
这篇关于比较Haskell和Scala绑定/平面地图示例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!