.tail
Замечу, что в вашем классе Cons
уже есть член publi c tail
. Я хотел бы начать там и сделать его универсальным ...
sealed trait MyList[+A] {
def tail: MyList[A]
}
... и добавить реализацию MyNil
.
case object MyNil extends MyList[Nothing] {
def tail: MyList[Nothing] =
throw new java.lang.UnsupportedOperationException("tail of empty list")
}
Вот как стандарт библиотека List
обрабатывает хвост пустого списка. Другим, возможно, более мягким вариантом будет возвращение this
, так что хвост пустого MyList
будет просто пустым MyList
.
Оставив class Cons
и object MyList
без изменений, мы получим ожидаемые результаты.
MyList('s','h','o','w').tail //res0: MyList[Char] = Cons(h,Cons(o,Cons(w,MyNil)))
MyList(9).tail.tail //java.lang.Unsupported...
.sum
Это немного сложнее. Мы хотим, чтобы каждый вызов .sum
компилировал только , если элементы имеют суммируемый тип, такой как Int
. Scala способ добиться этого, требующий, чтобы сайт вызова предоставил неявные "доказательства" того, что тип элемента приемлем.
sealed trait MyList[+A] {
def sum(implicit ev : A =:= Int) : Int //can sum only if A is Int
}
Увы, это не скомпилируется, потому что MyList
является ковариантным для A
, но, будучи типом переданного параметра, ставит A
в противоположную позицию.
Ошибка: ковариантный тип A встречается в инвариантной позиции в типе A =: = Int значения ev
К счастью, есть исправление: используйте параметр другого типа, связанный с A
, но не ограниченный его ковариантным отношением.
sealed trait MyList[+A] {
def sum[B >: A](implicit ev : B =:= Int) : Int = 0 //default behavior
}
case object MyNil extends MyList[Nothing] { ... //unchanged
case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A] {
override def sum[B >: A](implicit ev :B =:= Int) : Int = head + tail.sum[B]
}
object MyList { ... //unchanged
MyList(23,31,12).sum //res0: Int = 66
MyList("as","is").sum //won't compile
Числовой [A]
Хорошо, это работает для Int
, но было бы больно делать то же самое для каждого суммируемого типа. К счастью, стандартная библиотека предлагает класс типов Numeric
, который предоставляет некоторые базовые значения c (zero
и one
) и операции (plus()
, minus()
, times()
, et c.) Для всех цифры c печатаются под зонтиком (Short
, Long
, Float
, et c.).
Итак, все вместе:
sealed trait MyList[+A] {
val tail: MyList[A]
def sum[B >: A](implicit ev : Numeric[B]): B = ev.zero
}
case object MyNil extends MyList[Nothing] {
val tail: MyList[Nothing] = this
}
case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A] {
override def sum[B >: A](implicit ev : Numeric[B]): B = ev.plus(head, tail.sum[B])
}
object MyList {
def apply[A](as: A*): MyList[A] =
if (as.isEmpty) MyNil else Cons(as.head, apply(as.tail: _*))
}