Digite Lambda no limite de contexto e a função do alias do tipo

Sep 19 2020

Tive que escrever um contexto vinculado ao Pedido [Opção [T]]

Acontece que a solução foi

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))
}

veja Como definir um contexto vinculado a um tipo de tipo superior (construtor de tipo)

Então brinquei um pouco com o tipo lambda para entender melhor, levando-me a escrever a versão sem o tipo lambda:

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

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

Muitos exemplos do uso do tipo lambda são para construtor de tipo com 2 parâmetros como MAP [K, V].

Nesse caso, não temos esse problema.

Então eu só me pergunto, por que não ter algo assim

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

obviamente não funciona. Acho que entendi tudo, não há nenhum construtor de tipo Ordering[Option[_]]definido.

O que temos em ordem é:

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 }

Como está Ordering[Option[T]]na definição acima, é Ordering[Option[T]] forSome {type T}akka Existencial, portanto, um tipo apropriado, e não um construtor de tipo.

Então, se eu estiver correto, o que fazemos aqui:

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

ou aqui:

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

está definindo um Construtor de tipo Ordering[Option[_]].

Questão 1:

1 - o meu entendimento está correto? é isso que o Type Lambda faz aqui?

2 - Estou um pouco confuso aqui, então alias de tipo é algo que permite criar construtor de tipo a partir da composição de outros construtores de tipo. Em certo sentido, estou tentando entender a função formal do alias de tipo com variável de tipo.

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> 

Respostas

3 MarioGalic Sep 19 2020 at 04:33

Construtor de tipo anônimo

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

é para o construtor de tipo nomeado

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

que construtor anônimo (valor)

(x: Int) => x + 1

é para o construtor nomeado (valor)

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

por exemplo

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

Em Scala 3 (Dotty) você poderá substituir a "atrocidade"

Há menos necessidade de um tipo de projetor no dotty de qualquer maneira, porque temos lambdas de tipo nativo em vez da atrocidade envolvendo tipos estruturais.

com bela sintaxe lambda de tipo

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]]

A sintaxe ligada ao contexto

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

é equivalente a

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

que simplifica para

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