scala> val w = new ::[Int](4,Nil)
w: scala.collection.immutable.::[Int] = List(4)
scala> val x = List[Int](4)
x: List[Int] = List(4)
这两种表达有什么区别?::[Int](4)
是List[int]
的实例,因为::[Int](4)
和List[Int](4)
都List(4)
?
::[T]
是代数数据类型List[T]
的构造函数之一。另一个是Nil
。考虑以下List
的归纳定义
sealed trait List[+T]
case object Nil extends List[Nothing]
case class ::[T](head: T, tail: List[T]) extends List[T]
::
可能看起来有点奇怪,因为它纯粹是象征性名称,在其他语言中,它有时被称为Cons
.构造函数是制作类型List[T]
值的方法,这里有一些
Nil
::(1, Nil)
::(2, ::(1, Nil))
::(42, ::(2, ::(1, Nil)))
这些值具有归纳性质,具有显着的性质,即每当我们有一个值时,我们总是知道如何构造以下值。换句话说,归纳值意味着归纳值。例如,两者
::(2, ::(1, Nil))
和
::(51, ::(1, Nil))
是构造函数::[Int]
的力量::(1, Nil)
的含义。
::[Int]
和List[Int]
一样吗?
因为 Scala 使用继承形式实现代数数据类型,所以确实存在is-a-subtype-of关系
implicitly[::[Int] <:< List[Int]] // ok!
虽然不存在平等关系
implicitly[::[Int] =:= List[Int]] // error!
然而,在我看来,这些子类型关系不是代数数据类型的重点,在 Scala 中是偶然的。相反,构造函数::[T]
与其构造的值的类型List[T]
之间的关系更类似于函数和类型之间的关系。
new ::[Int](4, Nil)
和List[Int](4)
一样吗?
它们的计算结果都相同,例如以下传递
assert(new ::[Int](4, Nil) == List[Int](4))
然而,我们得出这个值的方式是完全不同的。在前一种情况下,我们直接使用List[Int]
ADT 的构造函数,而在后一种情况下,我们实际上是在trait List[T]
的伴随对象上调用apply
方法
List.apply[Int](4)
其中apply
定义为
def apply[A](xs: A*): List[A] = xs.toList
现在在实践中,我们很少需要考虑这些事情。通常我们只是写一些类似的东西
List(4, 2, 42)
并完成它。"低级"构造函数::
有时会在模式匹配期间显示其面孔,这是一种解构ADT 的方法,例如,这里是递归实现,它获取列表中元素的总和
def sum(l: List[Int]): Int = {
l match {
case Nil => 0
case head :: tail => head + sum(tail)
}
}
sum(List(1, 2, 42))
// res1: Int = 45
abstract class List
是密封的,所以它只由类::[+A]
和object Nil
扩展。
这意味着,为了符合type List[+A]
,一个对象必须是type ::[X]
的,其中X
符合A
,或者Nil
符合List[Nothing]
。
值type List[Int]
很可能是Nil
的,因为List[Nothing]
符合List[Int]
。另一方面,您不能将Nil
赋值type ::[Int]
,因为它不是::[_]
。
TL;博士: 每个::[_]
都是一个List[_]
,但不是每个List[_]
都是::[_]
。