我正在关注本书Functional programming in Scala
,特别是您实现简单Stream特征和伴随对象的部分。作为参考,这是我们迄今为止在伴侣对象中所拥有的内容
object Stream {
def empty[A]: Stream[A] =
new Stream[A] {
def uncons = None
}
def cons[A](hd: => A, tl: => Stream[A]): Stream[A] =
new Stream[A] {
lazy val uncons = Some((hd, tl))
}
def apply[A](as: A*): Stream[A] =
if (as.isEmpty)
empty
else
cons(as.head, apply(as.tail: _*))
}
到目前为止的特点:
trait Stream[A] {
import Stream._
def uncons: Option[(A, Stream[A])]
def toList: List[A] = uncons match {
case None => Nil: List[A]
case Some((a, as)) => a :: as.toList
}
def #::(a: => A) = cons(a, this)
def take(n: Int): Stream[A] =
if (n <= 0)
empty
else (
uncons
map { case (a, as) => a #:: (as take (n - 1)) }
getOrElse empty
)
}
下一个练习要求我为takeWhile
编写一个实现,我认为以下是
def takeWhile(f: A => Boolean): Stream[A] = (
uncons
map { case (a, as) => if (f(a)) (a #:: (as takeWhile f)) else empty }
getOrElse empty
)
不幸的是,似乎我得到了一个我无法追踪的差异错误:
error: type mismatch; found : Stream[_2] where type _2 <: A
required: Stream[A]
Note: _2 <: A, but trait Stream is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
getOrElse empty
^
我可以添加方差注释,但在此之前我想了解这里出了什么问题。有什么建议吗?
答案 0 :(得分:2)
这似乎是类型推断的问题,因为如果您明确指定子表达式uncons map { case (a, as) => if (f(a)) (a #:: (as takeWhile f)) else empty }
的类型,它就有效。
def takeWhile(f: A => Boolean): Stream[A] = {
val mapped:Option[Stream[A]] = uncons map {
case (a, as) => if (f(a)) (a #:: (as takeWhile f)) else empty
}
mapped getOrElse empty
}
答案 1 :(得分:1)
要完成另一个答案,此行empty
:
map { case (a, as) => if (f(a)) (a #:: (as takeWhile f)) else empty }
被推断为empty[Nothing]
,这意味着(a #:: (as takeWhile f)) else empty
被推断为Stream[Foo <: A]
,并且因为Stream[A]
是预期的而Stream
是不变的,所以你有一个错误。
因此,这为我们提供了解决此问题的最简洁方法:只需注释empty
:
map { case (a, as) => if (f(a)) (a #:: (as takeWhile f)) else empty[A] }
然后它编译得很好。
原始Stream
不会发生这种情况,因为它是协变的,因此您实际上要求Stream.empty
成为Stream[Nothing]
(就像Nil
一样{{1} 1}}),或者你不在乎。
现在,关于它被推断为List[Nothing]
而不是empty[Nothing]
的确切原因,这可能隐藏在SLS 6.26.4“本地类型推断”的某处,但这一部分实际上不能被指责为容易阅读...
作为一项规则,一旦你打电话给方法,总是要怀疑: