KEMBAR78
Type-level programming | PDF
Type-level programming
Dmytro Mitin
https://stepik.org/course/Introduction-to-programming-
with-dependent-types-in-Scala-2294/
March 2017
Dmytro Mitin Type-level programming
Type-level programming
Value level Type level
ADT values: val x object X
Members: def x, val x type X
def f(x) type F[X]
a.b A#B
x : T X <: T
new A(b) A[B]
Dmytro Mitin Type-level programming
Value-level booleans
sealed trait Bool {
def not: Bool
def &&(b: Bool): Bool
def ||(b: Bool): Bool
def ifElse[C](t: => C, f: => C): C
}
case object True extends Bool {
def not: Bool = False
def &&(b: Bool): Bool = b
def ||(b: Bool): Bool = True
def ifElse[C](t: => C, f: => C): C = t
}
case object False extends Bool {
def not: Bool = True
def &&(b: Bool): Bool = False
def ||(b: Bool): Bool = b
def ifElse[C](t: => C, f: => C): C = f
} Dmytro Mitin Type-level programming
Type-level booleans
sealed trait Bool {
type Not <: Bool
type && [B <: Bool] <: Bool
type || [B <: Bool] <: Bool
type IfElse[C, T <: C, F <: C] <: C
}
type True = True.type
type False = False.type
case object True extends Bool {
type Not = False
type && [B <: Bool] = B
type || [B <: Bool] = True
type IfElse[C, T <: C, F <: C] = T
}
Dmytro Mitin Type-level programming
Type-level booleans
case object False extends Bool {
type Not = True
type && [B <: Bool] = False
type || [B <: Bool] = B
type IfElse[C, T <: C, F <: C] = F
}
implicitly[False# Not =:= True]
implicitly[(True# && [False]) =:= False]
implicitly[(True# || [False]) =:= True]
implicitly[False# IfElse[Any, Int, String] =:= String]
// compile time!
Dmytro Mitin Type-level programming
Value-level natural numbers
sealed trait Nat {
def ++ : Nat = Succ(this)
def +(x: Nat): Nat
def *(x: Nat): Nat
}
case object Zero extends Nat {
def +(x: Nat): Nat = x
def *(x: Nat): Nat = Zero
}
case class Succ(n: Nat) extends Nat {
def +(x: Nat): Nat = Succ(n.+(x))
def *(x: Nat): Nat = n.*(x).+(x)
}
Dmytro Mitin Type-level programming
Type-level natural numbers
sealed trait Nat {
type This >: this.type <: Nat
type ++ = Succ[This]
type + [ <: Nat] <: Nat
type * [ <: Nat] <: Nat
}
final object Zero extends Nat {
type This = Zero
type + [X <: Nat] = X
type * [ <: Nat] = Zero
}
type Zero = Zero.type
final class Succ[N <: Nat] extends Nat {
type This = Succ[N]
type + [X <: Nat] = Succ[N# + [X]]
type * [X <: Nat] = (N# * [X])# + [X]
}
Dmytro Mitin Type-level programming
Type-level natural numbers
type 0 = Zero
type 1 = 0 # ++
type 2 = 1 # ++
type 3 = 2 # ++
type 4 = 3 # ++
type 5 = 4 # ++
type 6 = 5 # ++
type 7 = 6 # ++
type 8 = 7 # ++
implicitly[False# IfElse[Nat, 2, 4] =:= 4]
implicitly[ 2# + [ 3] =:= 5]
implicitly[ 2# * [ 3] =:= 6]
Dmytro Mitin Type-level programming
Value-level factorial and Fibonacci numbers
def factorial(n: Nat): Nat = n match {
case Zero => Succ(Zero)
case Succ(m) =>
val x = factorial(m)
x.*(Succ(m))
}
def fibonacci(n: Nat): Nat = n match {
case Zero => Zero
case Succ(Zero) => Succ(Zero)
case Succ(Succ(m)) =>
val x = fibonacci(m)
val y = fibonacci(Succ(m))
x.+(y)
}
Dmytro Mitin Type-level programming
Type-level factorial
sealed trait Factorial[N <: Nat] { type Res <: Nat }
implicit object factorial0 extends Factorial[ 0] { type Res = 1 }
implicit def factorial[N <: Nat, X <: Nat](implicit
fact: Factorial[N] { type Res = X }
) = new Factorial[Succ[N]] { type Res = X# * [Succ[N]] }
implicitly[Factorial[ 0] { type Res = 1 }]
implicitly[Factorial[ 1] { type Res = 1 }]
implicitly[Factorial[ 2] { type Res = 2 }]
implicitly[Factorial[ 3] { type Res = 6 }]
Dmytro Mitin Type-level programming
Type-level Fibonacci numbers
sealed trait Fibonacci[N <: Nat] { type Res <: Nat }
implicit object fibonacci0 extends Fibonacci[ 0] { type Res = 0 }
implicit object fibonacci1 extends Fibonacci[ 1] { type Res = 1 }
implicit def fibonacci[N <: Nat, X <: Nat, Y <: Nat](implicit
fib1: Fibonacci[N] { type Res = X },
fib2: Fibonacci[Succ[N]] { type Res = Y }
) = new Fibonacci[Succ[Succ[N]]] { type Res = X# + [Y] }
implicitly[Fibonacci[ 0] { type Res = 0 }]
implicitly[Fibonacci[ 1] { type Res = 1 }]
implicitly[Fibonacci[ 2] { type Res = 1 }]
implicitly[Fibonacci[ 3] { type Res = 2 }]
implicitly[Fibonacci[ 4] { type Res = 3 }]
implicitly[Fibonacci[ 5] { type Res = 5 }]
implicitly[Fibonacci[ 6] { type Res = 8 }]
Dmytro Mitin Type-level programming
Shapeless
build.sbt
libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.2"
import shapeless.Nat.
val x: 0 = 0
val y: 1 = 1
val z: 2 = 2
val t: 3 = 3
// ...
Dmytro Mitin Type-level programming

Type-level programming

  • 1.
  • 2.
    Type-level programming Value levelType level ADT values: val x object X Members: def x, val x type X def f(x) type F[X] a.b A#B x : T X <: T new A(b) A[B] Dmytro Mitin Type-level programming
  • 3.
    Value-level booleans sealed traitBool { def not: Bool def &&(b: Bool): Bool def ||(b: Bool): Bool def ifElse[C](t: => C, f: => C): C } case object True extends Bool { def not: Bool = False def &&(b: Bool): Bool = b def ||(b: Bool): Bool = True def ifElse[C](t: => C, f: => C): C = t } case object False extends Bool { def not: Bool = True def &&(b: Bool): Bool = False def ||(b: Bool): Bool = b def ifElse[C](t: => C, f: => C): C = f } Dmytro Mitin Type-level programming
  • 4.
    Type-level booleans sealed traitBool { type Not <: Bool type && [B <: Bool] <: Bool type || [B <: Bool] <: Bool type IfElse[C, T <: C, F <: C] <: C } type True = True.type type False = False.type case object True extends Bool { type Not = False type && [B <: Bool] = B type || [B <: Bool] = True type IfElse[C, T <: C, F <: C] = T } Dmytro Mitin Type-level programming
  • 5.
    Type-level booleans case objectFalse extends Bool { type Not = True type && [B <: Bool] = False type || [B <: Bool] = B type IfElse[C, T <: C, F <: C] = F } implicitly[False# Not =:= True] implicitly[(True# && [False]) =:= False] implicitly[(True# || [False]) =:= True] implicitly[False# IfElse[Any, Int, String] =:= String] // compile time! Dmytro Mitin Type-level programming
  • 6.
    Value-level natural numbers sealedtrait Nat { def ++ : Nat = Succ(this) def +(x: Nat): Nat def *(x: Nat): Nat } case object Zero extends Nat { def +(x: Nat): Nat = x def *(x: Nat): Nat = Zero } case class Succ(n: Nat) extends Nat { def +(x: Nat): Nat = Succ(n.+(x)) def *(x: Nat): Nat = n.*(x).+(x) } Dmytro Mitin Type-level programming
  • 7.
    Type-level natural numbers sealedtrait Nat { type This >: this.type <: Nat type ++ = Succ[This] type + [ <: Nat] <: Nat type * [ <: Nat] <: Nat } final object Zero extends Nat { type This = Zero type + [X <: Nat] = X type * [ <: Nat] = Zero } type Zero = Zero.type final class Succ[N <: Nat] extends Nat { type This = Succ[N] type + [X <: Nat] = Succ[N# + [X]] type * [X <: Nat] = (N# * [X])# + [X] } Dmytro Mitin Type-level programming
  • 8.
    Type-level natural numbers type0 = Zero type 1 = 0 # ++ type 2 = 1 # ++ type 3 = 2 # ++ type 4 = 3 # ++ type 5 = 4 # ++ type 6 = 5 # ++ type 7 = 6 # ++ type 8 = 7 # ++ implicitly[False# IfElse[Nat, 2, 4] =:= 4] implicitly[ 2# + [ 3] =:= 5] implicitly[ 2# * [ 3] =:= 6] Dmytro Mitin Type-level programming
  • 9.
    Value-level factorial andFibonacci numbers def factorial(n: Nat): Nat = n match { case Zero => Succ(Zero) case Succ(m) => val x = factorial(m) x.*(Succ(m)) } def fibonacci(n: Nat): Nat = n match { case Zero => Zero case Succ(Zero) => Succ(Zero) case Succ(Succ(m)) => val x = fibonacci(m) val y = fibonacci(Succ(m)) x.+(y) } Dmytro Mitin Type-level programming
  • 10.
    Type-level factorial sealed traitFactorial[N <: Nat] { type Res <: Nat } implicit object factorial0 extends Factorial[ 0] { type Res = 1 } implicit def factorial[N <: Nat, X <: Nat](implicit fact: Factorial[N] { type Res = X } ) = new Factorial[Succ[N]] { type Res = X# * [Succ[N]] } implicitly[Factorial[ 0] { type Res = 1 }] implicitly[Factorial[ 1] { type Res = 1 }] implicitly[Factorial[ 2] { type Res = 2 }] implicitly[Factorial[ 3] { type Res = 6 }] Dmytro Mitin Type-level programming
  • 11.
    Type-level Fibonacci numbers sealedtrait Fibonacci[N <: Nat] { type Res <: Nat } implicit object fibonacci0 extends Fibonacci[ 0] { type Res = 0 } implicit object fibonacci1 extends Fibonacci[ 1] { type Res = 1 } implicit def fibonacci[N <: Nat, X <: Nat, Y <: Nat](implicit fib1: Fibonacci[N] { type Res = X }, fib2: Fibonacci[Succ[N]] { type Res = Y } ) = new Fibonacci[Succ[Succ[N]]] { type Res = X# + [Y] } implicitly[Fibonacci[ 0] { type Res = 0 }] implicitly[Fibonacci[ 1] { type Res = 1 }] implicitly[Fibonacci[ 2] { type Res = 1 }] implicitly[Fibonacci[ 3] { type Res = 2 }] implicitly[Fibonacci[ 4] { type Res = 3 }] implicitly[Fibonacci[ 5] { type Res = 5 }] implicitly[Fibonacci[ 6] { type Res = 8 }] Dmytro Mitin Type-level programming
  • 12.
    Shapeless build.sbt libraryDependencies += "com.chuusai"%% "shapeless" % "2.3.2" import shapeless.Nat. val x: 0 = 0 val y: 1 = 1 val z: 2 = 2 val t: 3 = 3 // ... Dmytro Mitin Type-level programming