Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Daniel Ciocîrlan - Type-Level Sorting in Scala

Shannon
March 28, 2019

Daniel Ciocîrlan - Type-Level Sorting in Scala

Abstract: Scala's type system and implicit resolution mechanism allow for sophisticated type-level relationship inference. In this talk, we will see how we can impose type-level constraints and determine complex relationships between types, enough to be able to sort types... at compile time.

Shannon

March 28, 2019
Tweet

More Decks by Shannon

Other Decks in Programming

Transcript

  1. Goal Show the power of Scala’s type system and implicit

    resolution Prerequisites • Scala implicits • Type aliases • A little bit of math… Operate on TYPES at compile time Note: I work at Palantir, but this talk is completely unrelated to my work there.
  2. trait Nat class _0 extends Nat class Succ[N <: Nat]

    extends Nat Ep 1: numbers as types We can’t define all natural numbers ”manually” Define natural numbers in terms of their succession object Nat { type _1 = Succ[_0] type _2 = Succ[_1] type _3 = Succ[_2] type _4 = Succ[_3] type _5 = Succ[_4] ... }
  3. class <[A <: Nat, B <: Nat] Ep 2: type

    ”comparison” We just kissed regular values goodbye Define the less-than relationship as a type realistic name <[_0,_1] is a type, not an expression _0 < _1 is sugar for <[_0,_1] Axioms of less-than: • _0 < Succ[N] for every natural N • Succ[A] < Succ[B] if and only if A < B
  4. val x: _2 < _4 = ... Ep 2: type

    ”comparison” The compiler should figure out if the type _2 < _4 exists : • that’s the same as Succ[_1] < Succ[_3] • does the first axiom work? no. • second axiom: Succ[_1] < Succ[3] exists only if _1 < _3 exists • same as Succ[_0] < Succ[_2] • does the first axiom work? no. • second axiom: Succ[_0] < Succ[_2] exists only if _0 < _2 exists • does the first axiom work? • YES! • so _0 < _2 exists • so _1 < _3 exists • so _2 < _4 exists! The type is fine! How do we figure out if a type exists ? Through implicits.
  5. implicit def basicAxiom[N <: Nat] = new <[_0, Succ[N]] Ep

    2: type ”comparison” object < { implicit def basicAxiom[N <: Nat] = new <[_0, Succ[N]] implicit def inductiveAxiom[A <: Nat, B <: Nat](implicit evidence: A < B) = new <[Succ[A], Succ[B]] } Read: for every type N derived from Nat, there is an instance of _0 < Succ[N]. implicit def inductiveAxiom[A <: Nat, B <: Nat](implicit evidence: A < B) = new <[Succ[A], Succ[B]] Read: for every type A and B derived from Nat, • if there is an instance of A < B • then there is an instance of Succ[A] < Succ[B]. same as <[A, B]
  6. Ep 3: Adding Numbers trait +[A <: Nat, B <:

    Nat] { type Result <: Nat } type Add[A <: Nat, B <: Nat, R <: Nat] = +[A, B] { type Result = R } Read: if someone gives me an instance of Add, I’ll consider it a + and I’ll store the type member Read: the type of an add operation of two numbers with the result stored as a type member (we’ll need this later)
  7. Ep 3: Adding Numbers Axiom 1: adding 0 gives back

    the same number Read: for every number, there is an instance of an add operation with zero and that number implicit def basic[A <: Nat]: Add[_0, A, A] = new +[_0, A] { type Result = A } implicit def inductive[A <: Nat, B <: Nat](implicit evidence: +[A, B]): Add[Succ[A], B, Succ[evidence.Result]] = new +[Succ[A], B] { type Out = Succ[evidence.Result] } Axiom 2: adding two random numbers Read: for every two ”numbers” A and B, if there is an instance of an add operation on A and B, • then there is an instance of an add operation on Succ[A] and B where • the result is the successor of adding A and B
  8. Ep 3: Adding Numbers Bringing the evidence to the surface

    (so we can see it clearly when we print its type tag) def apply[A <: Nat, B <: Nat](implicit evidence: +[A, B]): new +[A, B] { type Result = evidence.Result } def show[T](implicit tag: TypeTag[A]) = println(tag) show(+[_2, _3]) TypeTag[... { type Result = Succ[Succ[Succ[Succ[Succ[_0]]]]] }] Read: 5
  9. Ep 3: Adding Numbers But how? Is there an implicit

    value of type +[_2, _3]? show(+[_2, _3]) implicit def basic[A <: Nat]: Add[_0, A, A] = new +[_0, A] { type Result = A } implicit def inductive[A <: Nat, B <: Nat](implicit evidence: +[A, B]): Add[Succ[A], B, Succ[evidence.Result]] = new +[A, B] { type Out = Succ[evidence.Result] } No, but _2 = Succ[_1], so maybe I can look further
  10. Ep 3: Adding Numbers Is there an implicit value of

    type +[_1, _3]? implicit def basic[A <: Nat]: Add[_0, A, A] = new +[_0, A] { type Result = A } implicit def inductive[A <: Nat, B <: Nat](implicit evidence: +[A, B]): Add[Succ[A], B, Succ[evidence.Result]] = new +[A, B] { type Out = Succ[evidence.Result] } No, but _1 = Succ[_0], so maybe I can look further
  11. Ep 3: Adding Numbers Is there an implicit value of

    type +[_0, _3]? implicit def basic[A <: Nat]: Add[_0, A, A] = new +[_0, A] { type Result = A } Yes, and its result is _3! implicit def inductive[A <: Nat, B <: Nat](implicit evidence: +[A, B]): Add[Succ[A], B, Succ[evidence.Result]] = new +[A, B] { type Out = Succ[evidence.Result] }
  12. Ep 3: Adding Numbers So I can also build an

    implicit value of type +[Succ[_0], _3]: implicit def inductive[A <: Nat, B <: Nat](implicit evidence: +[A, B]): Add[Succ[A], B, Succ[evidence.Result]] = new +[A, B] { type Out = Succ[evidence.Result] } And its result is Succ[_3] I have an evidence of type +[_0, _3]
  13. Ep 3: Adding Numbers So I can also build an

    implicit value of type +[Succ[_1], _3]: implicit def inductive[A <: Nat, B <: Nat](implicit evidence: +[A, B]): Add[Succ[A], B, Succ[evidence.Result]] = new +[A, B] { type Out = Succ[evidence.Result] } And its result is Succ[Succ[_3]] So I can build an implicit of type +[_2, _3] and its result is Succ[Succ[_3]] show(+[_2, _3]) I have an evidence of type +[_1, _3]
  14. Ep 4: Subtracting Numbers trait -[A <: Nat, B <:

    Nat] { type Result <: Nat } type Subtract[A <: Nat, B <: Nat, R <: Nat] = -[A, B] { type Result = R } Read: if someone gives me an instance of Subtract, I’ll consider it a - and I’ll store the type member Read: the type of a subtraction of two numbers with the result stored as a type member (we’ll need this later)
  15. Axiom 1: subtracting 0 gives back the same number Read:

    for every number, there is an instance of a subtract operation with that number and zero implicit def basic[A <: Nat]: Subtract[A, _0, A] = new -[A, _0] { type Result = A } implicit def inductive[A <: Nat, B <: Nat](implicit evidence: -[A, B]) : Subtract[Succ[A], Succ[B], evidence.Result] = new +[Succ[A], B] { type Out = Succ[evidence.Result] } Axiom 2: subtracting two random numbers Read: for every two ”numbers” A and B, if there is an instance of a subtract operation on A and B, • then there is an instance of a subtract operation on Succ[A] and Succ[B] where • the result is the same as the subtraction of A and B Ep 4: Subtracting Numbers
  16. Bringing the evidence to the surface (so we can see

    it clearly when we print its type tag) def apply[A <: Nat, B <: Nat](implicit evidence: -[A, B]): new -[A, B] { type Result = evidence.Result } show(-[_5, _2]) TypeTag[... { type Result = Succ[Succ[Succ[_0]]] }] Read: 3 Ep 4: Subtracting Numbers
  17. implicit def inductive[A <: Nat, B <: Nat](implicit evidence: -[A,

    B]) : Subtract[Succ[A], Succ[B], evidence.Result] = new +[Succ[A], B] { type Out = Succ[evidence.Result] } implicit def basic[A <: Nat]: Subtract[A, _0, A] = new -[A, _0] { type Result = A } But how? Is there an implicit value of type -[_5, _2]? show(-[_5, _2]) No, but _2 = Succ[_1] and _5 = Succ[_4], so maybe I can look further Ep 4: Subtracting Numbers
  18. implicit def inductive[A <: Nat, B <: Nat](implicit evidence: -[A,

    B]) : Subtract[Succ[A], Succ[B], evidence.Result] = new +[Succ[A], B] { type Out = Succ[evidence.Result] } implicit def basic[A <: Nat]: Subtract[A, _0, A] = new -[A, _0] { type Result = A } Is there an implicit value of type -[_4, _1]? No, but _4 = Succ[_3] and _1 = Succ[_0], so maybe I can look further Ep 4: Subtracting Numbers
  19. implicit def inductive[A <: Nat, B <: Nat](implicit evidence: -[A,

    B]) : Subtract[Succ[A], Succ[B], evidence.Result] = new +[Succ[A], B] { type Out = Succ[evidence.Result] } implicit def basic[A <: Nat]: Subtract[A, _0, A] = new -[A, _0] { type Result = A } Is there an implicit value of type -[_3, _0]? Yes, and its result is _3! Ep 4: Subtracting Numbers
  20. implicit def inductive[A <: Nat, B <: Nat](implicit evidence: -[A,

    B]) : Subtract[Succ[A], Succ[B], evidence.Result] = new +[Succ[A], B] { type Out = Succ[evidence.Result] } So I can also build an implicit value of type -[Succ[_3], Succ[_0]]: And its result is still _3 I have an evidence of type -[_3, _0] Ep 4: Subtracting Numbers
  21. So I can also build an implicit value of type

    -[Succ[_4], Succ[_1]]: And its result is still _3 I have an evidence of type -[_4, _1] implicit def inductive[A <: Nat, B <: Nat](implicit evidence: -[A, B]) : Subtract[Succ[A], Succ[B], evidence.Result] = new +[Succ[A], B] { type Out = Succ[evidence.Result] } So I can build an implicit of type -[_5, _2] and its result is _3 show(-[_5, _2]) Ep 4: Subtracting Numbers show(-[_2, _5]) Error: no implicit arguments found