На мой взгляд, два самых простых примера после Function
- это порядок и равенство.Тем не менее, первое не противоречит варианту в стандартной библиотеке Scala, а второе даже не существует в нем.Итак, я собираюсь использовать эквиваленты Scalaz: Order и Equal .
Далее мне нужна некоторая иерархия классов, желательно такая, которая знакома и, конечно,, оба понятия выше должны иметь смысл для этого.Если бы у Scala был Number
суперкласс всех числовых типов, это было бы идеально.К сожалению, у него такого нет.
Поэтому я попытаюсь сделать примеры с коллекциями.Для простоты давайте рассмотрим Seq[Int]
и List[Int]
.Должно быть ясно, что List[Int]
является подтипом Seq[Int]
, то есть List[Int] <: Seq[Int]
.
Итак, что мы можем с ним сделать?Во-первых, давайте напишем что-то, что сравнивает два списка:
def smaller(a: List[Int], b: List[Int])(implicit ord: Order[List[Int]]) =
if (ord.order(a,b) == LT) a else b
Теперь я собираюсь написать неявное Order
для Seq[Int]
:
implicit val seqOrder = new Order[Seq[Int]] {
def order(a: Seq[Int], b: Seq[Int]) =
if (a.size < b.size) LT
else if (b.size < a.size) GT
else EQ
}
С этими определениями яТеперь можно сделать что-то вроде этого:
scala> smaller(List(1), List(1, 2, 3))
res0: List[Int] = List(1)
Обратите внимание, что я прошу Order[List[Int]]
, но я передаю Order[Seq[Int]]
.Это означает, что Order[Seq[Int]] <: Order[List[Int]]
.Учитывая, что Seq[Int] >: List[Int]
, это возможно только из-за противоречивости.
Следующий вопрос: имеет ли смысл?
Давайте снова рассмотрим smaller
.Я хочу сравнить два списка целых чисел.Естественно, все, что сравнивает два списка, приемлемо, но какова логика того, что два Seq[Int]
являются приемлемыми?
Обратите внимание в определении seqOrder
, как сравниваемые вещи становятся параметрами к нему.Очевидно, что List[Int]
может быть параметром для чего-то ожидающего Seq[Int]
.Из этого следует, что что-то, что сравнивает Seq[Int]
, приемлемо вместо чего-то, что сравнивает List[Int]
: они оба могут использоваться с одинаковыми параметрами.
Как насчет обратного?Допустим, у меня был метод, который сравнивал только ::
(список минусов), который вместе с Nil
является подтипом List
.Я, очевидно, не мог использовать это, потому что smaller
вполне мог бы получить Nil
для сравнения.Отсюда следует, что Order[::[Int]]
нельзя использовать вместо Order[List[Int]]
.
Давайте перейдем к равенству и напишем для него метод:
def equalLists(a: List[Int], b: List[Int])(implicit eq: Equal[List[Int]]) = eq.equal(a, b)
Поскольку Order
расширяет Equal
, Я могу использовать это с тем же неявным выше:
scala> equalLists(List(4, 5, 6), List(1, 2, 3)) // we are comparing lengths!
res3: Boolean = true
Логика здесь та же самая.Все, что может сказать, являются ли два Seq[Int]
одинаковыми, очевидно, также может сказать, являются ли два List[Int]
одинаковыми.Из этого следует, что Equal[Seq[Int]] <: Equal[List[Int]]
, что верно, потому что Equal
противоречиво.