Shannon
March 28, 2019
200

# 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.

March 28, 2019

## Transcript

3. ### 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.
4. ### 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] ... }
5. ### 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
6. ### 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.
7. ### 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]

9. ### 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)
10. ### 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
11. ### 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
12. ### 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
13. ### 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
14. ### 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] }
15. ### 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]
16. ### 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]
17. ### 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)
18. ### 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
19. ### 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
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] } 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
21. ### 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
22. ### 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
23. ### 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
24. ### 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