Type Lambda on context bound and the role of type alias

281 views Asked by At

I had to write a context bound for Ordering[Option[T]]

it turns out that the solution was

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

see How to define a context bound with a higher kinded Type (Type Constructor)

So played with type lambda a little to understand better, leading me to write the version without type lambda:

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

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

Many example of the use of Type lambda are for type constructor with 2 parameters such MAP[K,V].

In this case we do not have that problem.

So i just wonder, why not having something like this

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

obviously it does not work. I think I understood the all point, there is no type constructor Ordering[Option[_]] defined.

What we have in ordering is:

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 }

As it stands Ordering[Option[T]] in the definition above, is Ordering[Option[T]] forSome {type T} akka Existential, therefore a proper type, and not a type constructor.

So if i am correct what we do here:

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

or here:

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

is defining a Type Constructor Ordering[Option[_]].

Question 1:

1 - is my understanding correct ? is that what the Type Lambda does here ?

2 - I'm a bit confused here, so type alias is something that allows you to create type constructor out of the composition of other type constructors. In a sense i am trying to understand the formal role of type alias with type variable.

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

There are 1 answers

0
Mario Galic On BEST ANSWER

Anonymous type constructor

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

is to named type constructor

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

what anonymous (value) constructor

(x: Int) => x + 1

is to named (value) constructor

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

for example

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

In Scala 3 (Dotty) you will be able to replace the "atrocity"

There's less need for kind projector in dotty anyway because we have native type lambdas instead of the atrocity involving structural types.

with beautiful type lambda syntax

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

The context bound syntax

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

is equivalent to

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

which simplifies to

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