1

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> 
Mario Galic
  • 47,285
  • 6
  • 56
  • 98
MaatDeamon
  • 9,532
  • 9
  • 60
  • 127
  • So `[X : F]` is expanded as `implicit ev: F[X]` - `Ordering[Option]` is not type constructor _(it does not look like `F[_]`)_ - the type lambda creates a new type `L[X] = Ordering[Option[X]]` which is a type constructor that looks like `F[_]` - A type alias is just a alias, a new name, to a new type; which in turn can be used to create aliases for type constructor, or even higher kinded types. – Luis Miguel Mejía Suárez Sep 18 '20 at 20:45
  • I think the type alias does a little more than just renaming espcially when it takes a type variable. I mean how can you create the type constructor ```Ordering[Option[_]]`` without the type alias ? – MaatDeamon Sep 18 '20 at 20:56
  • 3
    `Ordering[Option[_]]` is not a type constructor. – Mario Galic Sep 18 '20 at 20:59
  • `type L[X] = Ordering[Option[X]]` is just an alias for a type constructor. It is not doing anything else, the same way the type lambda is not doing anything else than exactly this but without creating a new alias. – Luis Miguel Mejía Suárez Sep 18 '20 at 21:01
  • I guess my point is, given how Ordering is defined which is `Trait Ordering[T]`, if it was not for the type alias, how else can you create the type constructor ```Ordering[Option[X]]``` or even express it ...... – MaatDeamon Sep 18 '20 at 21:04
  • 2
    Not sure what your point is, you need a type constructor _(let's call it `L`)_ that takes another type _(let's call it `X`)_ that is equal to `Ordering[Option[X]]`. Is not that different to writing equations in algebra. The language just need to provide a way to express such _"type function"_, and it turns out that such way is a type alias or a type lambda. The language could _(and IIRC **Dotty** wiil)_ provide a syntax like `X => Ordering[Option[X]]` to express that. – Luis Miguel Mejía Suárez Sep 18 '20 at 21:08
  • I think you just answer my question. Sorry if i was a bit confusing. That's precisely why i came here because i was confused. I never thought about type alias as something that sophisticated. I though it was just aliasing stuff, but no as you said above it does more: it allows you to express "type function". I needed something formal about it, something that name clearly what type alias can do. It felt like aliasing does not encompass the all functionality. – MaatDeamon Sep 18 '20 at 21:11
  • 1
    @LuisMiguelMejíaSuárez It will use arrow with two hats `=>>` like so `[X] =>> Ordering[Option[X]]` – Mario Galic Sep 18 '20 at 21:12
  • BTW now the term type lambda makes total sense :) – MaatDeamon Sep 18 '20 at 21:16
  • "type function" is def better than type alias. They should rename the construct too. – MaatDeamon Sep 18 '20 at 21:19
  • Still for me is just an alias a new name, the same way `y = f(x) = x + 1` here `f` is just a new name for the expression `x + 1`. – Luis Miguel Mejía Suárez Sep 18 '20 at 21:40
  • 1
    I hear u. So it is preference I guess. In any case, your statement with type function did it for me, so thanks – MaatDeamon Sep 18 '20 at 21:45

1 Answers1

4

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]]) = ???
Mario Galic
  • 47,285
  • 6
  • 56
  • 98