Slide 1

Slide 1 text

Rock the JVM! with Daniel Ciocîrlan

Slide 2

Slide 2 text

Type-Level Arithmetic in Scala Daniel Ciocîrlan rockthejvm.com

Slide 3

Slide 3 text

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.

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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.

Slide 7

Slide 7 text

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]

Slide 8

Slide 8 text

We’re starting to read implicits a little differently...

Slide 9

Slide 9 text

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)

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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]

Slide 16

Slide 16 text

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]

Slide 17

Slide 17 text

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)

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

All hail the Scala compiler!

Slide 26

Slide 26 text

Scala rocks Daniel Ciocîrlan rockthejvm.com