这个流的类型是如何铸造的?

时间:2016-04-23 06:43:32

标签: scala types casting type-conversion

我想知道下面的Stream类型是如何投放的?

type NTy = BigInt
def streamFib(n: Int): NTy = {
  lazy val fibs: Stream[NTy] = 1 #:: 1 #:: fibs.zip(fibs.tail).map(n => n._1 + n._2)
  fibs.drop(n - 1).head
}

此编译并streamFib返回BigInt类型(如预期的那样?)。

我还注意到了一些行为:

val s1:Stream[BigInt] = 1 #:: 2 #:: Stream.empty // incorrect, found Int required BigInt
val s2:Stream[BigInt] = 1 #:: 2 #:: Stream.empty[BigInt] // correct
val l1:List[BigInt] = 1 :: 2 :: List.empty[BigInt] // incorrect, found Any required BigInt
val l2:List[BigInt] = 1 :: BigInt(2) :: List.empty[BigInt] // correct

1 个答案:

答案 0 :(得分:3)

TL; DR:

fibs被定义为Stream[BigInt],因此当您向Int添加1 #:: ...时,编译器将从Int查找隐式转换} BigInt并在BigInt.int2bigInt

中找到它

这里有几件事情。

1)BigInt随播广告对象定义了从IntBigInt隐式转化

implicit def int2bigInt(i: Int): BigInt = apply(i)

这意味着,只要您需要BigInt,就可以提供Int,隐式转化将转换该值。您还可以说Int可以被视为 BigInt

2)以冒号结尾的方法是右关联。这意味着1 #:: 2 #:: Stream.empty[BigInt]可以有效地重写为Stream.empty[BigInt].#::(2).#::(1)

现在,如果您查看Stream.#::def #::(hd: A): Stream[A])的签名,您会看到Stream[BigInt].#::(x)只有在xBigInt时才能进行编译

当您致电1 #:: 2 #:: Stream.empty[BigInt]时,您呼叫Stream[BigInt].#::传递Int值而不是BigInt,但正如我之前提到的,Int可以被视为 BigInt,因此它们会自动转换为BigInts并且所有内容都会编译。

执行此操作时:val s1:Stream[BigInt] = 1 #:: 2 #:: Stream.empty 你正在做一个不同的事情:你正在右边创建一个Stream[Int]Stream.empty类型从你传递的1,2值推断为Int)然后你分配这个值到Stream[BigInt] val。

不幸的是,即使Stream[A]可以被视为 Stream[B],也没有从AB的隐式转换,因此编译失败

您可以通过以下方式定义自己的隐式转换:

implicit def asBigIntStream(xs: Stream[Int]): Stream[BigInt] = xs.map(BigInt.int2bigInt)
val s1:Stream[BigInt] = 1 #:: 2 #:: Stream.empty //This now works

List s还有其他问题:与Stream不同,List缺点定义为:

def ::[B >: A] (x: B): List[B]

使用Stream.#::(x),您需要x与您预先x前面的Stream完全相同。对于List.::(x)x(类型为B)可以是列表类型的超类型的实例。结果列表将是List[B],即在列表前面可以扩展其类型

因此,当您执行2 :: List.empty[BigInt]时,您有效地呼叫List[A].::(x: B),其中ABigInt,而B 被推断为{{ 1}}因为AnyAny最严格的超类型,也是BigInt 的超类型。

由于这使编译器满意,因此不会查找隐式转换,结果列表是List [Any],您不能再将其用作整数列表。

您基本上可以致电Int获取whatever :: List[X],其中List[Y]Y的最严格超类型和X的类型

那么,whatever为什么val l1:List[BigInt] = 1 :: 2 :: List.empty[BigInt]没有工作呢?

这是因为类型推断。让我们重写两个删除右关联性的表达式:

val l2 : List[BigInt] = 1 :: BigInt(2) :: List.empty[BigInt]

我不是百分百肯定的(如果我错了,请任何人纠正我)

  • 编译器只能在val l1: List[BigInt] = (List.empty[BigInt].::(2)).::(1) // incorrect, found Any required BigInt val l2: List[BigInt] = (List.empty[BigInt].::(BigInt(2))).::(1) // correct 的最后一个应用程序上帮助进行类型推断 在应用::之前,(List.empty[BigInt].::(2))已经List[Any],所以我们无能为力

  • .::(1)已经是(List.empty[BigInt].::(BigInt(2))),编译器可以尝试将List[BigInt]设为.::(1)(从而寻找隐式转换)