::[T]
является одним из конструкторов типа данных алгебра c 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]
::
может показаться немного странным, поскольку это чисто символическое c имя, а в других языках это иногда обозначается как Cons
. Конструкторы являются средством создания значений типа List[T]
, вот некоторые из них
Nil
::(1, Nil)
::(2, ::(1, Nil))
::(42, ::(2, ::(1, Nil)))
Эти значения, будучи индуктивными, обладают замечательным свойством всякий раз, когда мы их имеем, мы всегда знаем, как построить следующий один (и). Другими словами, индуктивные значения подразумевают индуктивные значения. Например, и
::(2, ::(1, Nil))
и
::(51, ::(1, Nil))
являются значениями ::(1, Nil)
силой конструктора ::[Int]
.
Is ::[Int]
так же, как List[Int]
?
Поскольку Scala реализует алгебраические c типы данных, используя форму наследования , действительно существует is-a -подтип-из отношений
implicitly[::[Int] <:< List[Int]] // ok!
пока не существует равно-1052 * отношения
implicitly[::[Int] =:= List[Int]] // error!
Однако, на мой взгляд, эти отношения подтипов это не точка алгебры c, тип данных является случайным в Scala. Вместо этого отношения между конструктором ::[T]
и типом List[T]
, значения которого он создает, более аналогичны отношениям между функцией и типом .
Является ли new ::[Int](4, Nil)
таким же, как List[Int](4)
?
Они оба оценивают одно и то же значение, например, следующие проходы
assert(new ::[Int](4, Nil) == List[Int](4))
однако способ, которым мы достигаем этого значения, совершенно другой. В первом случае мы используем непосредственно конструктор List[Int]
ADT, тогда как во втором случае мы фактически вызываем метод apply
для сопутствующего объекта trait List[T]
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