Тип Lambda в привязке к контексту и роли псевдонима типа

Sep 19 2020

Мне пришлось написать контекстную привязку для заказа [Option [T]]

оказывается, что решение было

def test[T: ({type L[x] = Ordering[Option[x]]})#L](value1: Option[T], value2: Option[T]) = {
  val e = implicitly(Ordering[Option[T]].compare(value1, value2))
}

см. Как определить контекст, связанный с более высокодородным типом (конструктор типов)

Поэтому немного поигрался с лямбда-типом, чтобы лучше понять, что привело меня к написанию версии без лямбда-типа:

type L[x] = Ordering[Option[x]]

def testN[T: L](value1: Option[T], value2: Option[T]) = {
  implicitly[L[T]].compare(value1, value2)
}

Многие примеры использования лямбда типа относятся к конструктору типов с двумя параметрами, например MAP [K, V].

В данном случае у нас нет такой проблемы.

Так что мне просто интересно, почему бы не иметь что-то подобное

def test[T: Ordering[Option]](value1: Option[T], value2: Option[T]) = {
  val e = implicitly(Ordering[Option[T]].compare(value1, value2))
}

очевидно, что это не работает. Думаю, я понял все, что конструктор типа не Ordering[Option[_]]определен.

Что мы имеем в заказе:

trait OptionOrdering[T] extends Ordering[Option[T]] {
    def optionOrdering: Ordering[T]
    def compare(x: Option[T], y: Option[T]) = (x, y) match {
      case (None, None)       => 0
      case (None, _)          => -1
      case (_, None)          => 1
      case (Some(x), Some(y)) => optionOrdering.compare(x, y)
    }
  }
  implicit def Option[T](implicit ord: Ordering[T]): Ordering[Option[T]] =
    new OptionOrdering[T] { val optionOrdering = ord }

Как Ordering[Option[T]]указано в приведенном выше определении, он Ordering[Option[T]] forSome {type T}akka Existential, следовательно, правильный тип, а не конструктор типа.

Итак, если я прав, что мы здесь делаем:

({type L[x] = Ordering[Option[x]]})#L

или здесь:

type L[x] = Ordering[Option[x]]

определяет конструктор типа Ordering[Option[_]].

Вопрос 1:

1 - я правильно понимаю? это то, что здесь делает лямбда типа?

2 - Я немного запутался здесь, поэтому псевдоним типа - это то, что позволяет вам создавать конструктор типа из композиции других конструкторов типов. В некотором смысле я пытаюсь понять формальную роль псевдонима типа с переменной типа.

scala> type e0 = Ordering[Option[_]]
defined type alias e0

scala> :kind -v e0
e0's kind is A
*
This is a proper type.
scala> type e1[w] = Ordering[Option[w]]
defined type alias e1

scala> :kind -v e1
e1's kind is F[A]
* -> *
This is a type constructor: a 1st-order-kinded type.

scala> 

Ответы

3 MarioGalic Sep 19 2020 at 04:33

Конструктор анонимного типа

({type L[x] = Ordering[Option[x]]})#L

для конструктора именованного типа

type L[x] = Ordering[Option[x]]

какой анонимный (значение) конструктор

(x: Int) => x + 1

для именованного (значения) конструктора

val f = (x: Int) => x + 1

например

scala> lazy val v: (({type L[x] = Ordering[Option[x]]})#L)[Int] = ???
lazy val v: scala.math.Ordering[Option[Int]] // unevaluated

scala> lazy val v: L[Int] = ???
lazy val v: L[Int] // unevaluated

scala> lazy val v = ((x: Int) => x + 1)(41)
lazy val v: Int // unevaluated

scala> lazy val v = f(41)
lazy val v: Int // unevaluated

В Scala 3 (Dotty) вы сможете заменить «зверство»

В любом случае, в dotty меньше необходимости, потому что у нас есть лямбды нативных типов вместо злодеяний, связанных со структурными типами.

с красивым синтаксисом лямбда типов

Starting dotty REPL...
scala> lazy val v: (({type L[x] = Ordering[Option[x]]})#L)[Int] = ???
lazy val v: Ordering[Option[Int]]

scala> lazy val v: ([x] =>> Ordering[Option[x]])[Int] = ???
lazy val v: Ordering[Option[Int]]

Синтаксис, связанный с контекстом

def test[T: ({type L[x] = Ordering[Option[x]]})#L](value1: Option[T], value2: Option[T]) = ???

эквивалентно

def test[T](value1: Option[T], value2: Option[T])(implicit ev: (({type L[x] = Ordering[Option[x]]})#L)[T]) = ???

что упрощает

def test[T](value1: Option[T], value2: Option[T])(implicit ev: Ordering[Option[T]]) = ???