type-level computations in scala · type%level)programming)in)scala 40 sealed abstract class hlist...

50
Type-Level Computations in Scala Stefan Zeiger

Upload: others

Post on 26-Sep-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Type-Level Computations in Scala Stefan Zeiger

Page 2: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Motivation

•  Heterogeneous collection types (HList, HArray)

•  Unlike Scala's tuples: • No size limit • Can abstract over arity

•  Requirement: No runtime overhead • So no implicits!

Type-­‐Level  Programming  in  Scala   2  

val l1 = 42 :: "foo" :: Some(1.0) :: "bar" :: HNil val i: Int = l1.head val s: String = l1.tail.head

Page 3: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Down The Rabbit Hole

Type-­‐Level  Programming  in  Scala   3  

Page 4: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Booleans

Page 5: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

A Simple Boolean Type

Type-­‐Level  Programming  in  Scala   5  

sealed trait Bool { def && (b: Bool): Bool def || (b: Bool): Bool def ifElse[B](t: => B, f: => B): B } lazy val True: Bool = new Bool { def && (b: Bool) = b def || (b: Bool) = True def ifElse[B](t: => B, f: => B) = t } lazy val False: Bool = new Bool { def && (b: Bool) = False def || (b: Bool) = b def ifElse[B](t: => B, f: => B) = f }

There's a Smalltalk in your Scala!

Term-Level

Page 6: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

From Values to Types

Type-­‐Level  Programming  in  Scala   6  

sealed trait Bool { } lazy val True: Bool = new Bool ... lazy val False: Bool = new Bool ...

Page 7: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

From Values to Types

Type-­‐Level  Programming  in  Scala   7  

sealed trait Bool { } object True extends Bool ... object False extends Bool ...

: True.type  

: False.type  

Page 8: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

From Values to Types

Type-­‐Level  Programming  in  Scala   8  

sealed trait Bool { } object True extends Bool ... object False extends Bool ... type True = True.type type False = False.type

: True.type  

: False.type  

Page 9: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Translating Methods

Type-­‐Level  Programming  in  Scala   9  

sealed trait Bool { def && (b: Bool): Bool def || (b: Bool): Bool def ifElse[B](t: => B, f: => B): B }

Page 10: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Translating Methods

Type-­‐Level  Programming  in  Scala   10  

sealed trait Bool { def && (b: Bool): Bool def || (b: Bool): Bool def ifElse[B](t: => B, f: => B): B } sealed trait Bool { type && type || }

Page 11: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Translating Methods

Type-­‐Level  Programming  in  Scala   11  

sealed trait Bool { def && (b: Bool): Bool def || (b: Bool): Bool def ifElse[B](t: => B, f: => B): B } sealed trait Bool { type && [B <: Bool] <: Bool type || [B <: Bool] <: Bool }

Page 12: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Translating Methods

Type-­‐Level  Programming  in  Scala   12  

sealed trait Bool { def && (b: Bool): Bool def || (b: Bool): Bool def ifElse[B](t: => B, f: => B): B } sealed trait Bool { type && [B <: Bool] <: Bool type || [B <: Bool] <: Bool type IfElse[B, T <: B, F <: B] <: B }

Page 13: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Translating Methods

Type-­‐Level  Programming  in  Scala   13  

sealed trait Bool { type && [B <: Bool] <: Bool type || [B <: Bool] <: Bool type IfElse[B, T <: B, F <: B] <: B } object True extends Bool { type && [B <: Bool] = B type || [B <: Bool] = True type IfElse[B, T <: B, F <: B] = T } object False extends Bool { type && [B <: Bool] = False type || [B <: Bool] = B type IfElse[B, T <: B, F <: B] = F }

Page 14: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Testing

Type-­‐Level  Programming  in  Scala   14  

assert ( (False && False ) == False ) assert ( (False && True ) == False ) assert ( (True && False ) == False ) assert ( (True && True ) == True ) assert ( (False || False ) == False ) assert ( (False || True ) == True ) assert ( (True || False ) == True ) assert ( (True || True ) == True ) assert ( False. ifElse (1, 2) == 2 ) assert ( True. ifElse (1, 2) == 1 )

Page 15: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Testing

Type-­‐Level  Programming  in  Scala   15  

assert ( (False && False ) == False ) assert ( (False. && (False)) == False ) (False# && [False])

A B C à B[A, C]  

a b c à a.b(c)  

implicitly[ =:= False ]

Page 16: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Testing

Type-­‐Level  Programming  in  Scala   16  

assert ( (False. && (False)) == False ) assert ( (False. && (True )) == False ) assert ( (True. && (False)) == False ) assert ( (True. && (True )) == True ) assert ( (False. || (False)) == False ) assert ( (False. || (True )) == True ) assert ( (True. || (False)) == True ) assert ( (True. || (True )) == True ) assert ( False. ifElse[Int](1, 2) == 2 ) assert ( True. ifElse[Int](1, 2) == 1 )

Page 17: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Testing

Type-­‐Level  Programming  in  Scala   17  

implicitly[ (False# && [False]) =:= False ] implicitly[ (False# && [True ]) =:= False ] implicitly[ (True# && [False]) =:= False ] implicitly[ (True# && [True ]) =:= True ] implicitly[ (False# || [False]) =:= False ] implicitly[ (False# || [True ]) =:= True ] implicitly[ (True# || [False]) =:= True ] implicitly[ (True# || [True ]) =:= True ] implicitly[ False# IfElse[Any, Int, String] =:= String ] implicitly[ True# IfElse[Any, Int, String] =:= Int ]

Page 18: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Natural Numbers

Page 19: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Natural Numbers

Type-­‐Level  Programming  in  Scala   19  

sealed trait Nat { def ++ = new Succ(this) } final object Zero extends Nat { } final class Succ(n: Nat) extends Nat { } val _0 = Zero val _1 = _0.++ val _2 = _1.++

Peano Numbers

Page 20: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Natural Numbers

Type-­‐Level  Programming  in  Scala   20  

sealed trait Nat { type ++ = Succ[this.type] } final object Zero extends Nat { } type Zero = Zero.type final class Succ[N <: Nat] extends Nat { } type _0 = Zero type _1 = _0 # ++ type _2 = _1 # ++

!

Page 21: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Natural Numbers

Type-­‐Level  Programming  in  Scala   21  

sealed trait Nat { type This >: this.type <: Nat type ++ = Succ[This] } final object Zero extends Nat { type This = Zero } type Zero = Zero.type final class Succ[N <: Nat] extends Nat { type This = Succ[N] } type _0 = Zero type _1 = _0 # ++ type _2 = _1 # ++

Page 22: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Testing

Type-­‐Level  Programming  in  Scala   22  

assert ( False. ifElse[Int]( 1, 2) == 2 ) assert ( True. ifElse[Int]( 1, 2) == 1 ) implicitly[ False# IfElse[Nat, _1, _2] =:= _2 ] implicitly[ True# IfElse[Nat, _1, _2] =:= _1 ]

Page 23: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Translation to Types

•  ADTs (Algebraic Data Types)

•  Different basic values and classes become types

•  Purely-functional design

•  Polymorphic dispatch (on receiver) • No match, if...else, etc.

Type-­‐Level  Programming  in  Scala   23  

Page 24: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Translation Rules

•  ADT Values: val è object

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

Type-­‐Level  Programming  in  Scala   24  

Page 25: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Recursion

Page 26: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Thinking Recursively: Addition

Type-­‐Level  Programming  in  Scala   26  

sealed trait Nat { type + [_ <: Nat] <: Nat }

final object Zero extends Nat { type + [X <: Nat] = X }

final class Succ[N <: Nat] extends Nat { type + [X <: Nat] = Succ[N # + [X]] }

Page 27: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Thinking Recursively: Multiplication

Type-­‐Level  Programming  in  Scala   27  

sealed trait Nat { type * [_ <: Nat] <: Nat } final object Zero extends Nat { type * [X <: Nat] = Zero } final class Succ[N <: Nat] extends Nat { type * [X <: Nat] = (N # * [X]) # + [X] }

Page 28: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Thinking Recursively: Exponentiation

Type-­‐Level  Programming  in  Scala   28  

sealed trait Nat { type ^ [X <: Nat] = } final object Zero extends Nat { } final class Succ[N <: Nat] extends Nat { }

^ is not commutative

(order matters)

X # Flip_^ [This] type Flip_^ [_ <: Nat] <: Nat type Flip_^ [X <: Nat] = _1 type Flip_^ [X <: Nat] = (N # Flip_^ [X]) # * [X]

Page 29: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Thinking Recursively: Folds

Type-­‐Level  Programming  in  Scala   29  

sealed trait Nat { def fold[U](f: U => U, z: => U): U } final object Zero extends Nat { def fold[U](f: U => U, z: => U) = z } final class Succ(n: Nat) extends Nat { def fold[U](f: U => U, z: => U) = f(n.fold(f, z)) }

Church Numerals def + (x: Nat): Nat = fold[Nat]((n => new Succ(n)), x)

Page 30: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Type Functions

Type-­‐Level  Programming  in  Scala   30  

sealed trait Nat { def fold[U](f: U => U, z: => U): U }

sealed trait Nat { type Fold[U, F[_ <: U] <: U, Z <: U] <: U } final object Zero extends Nat { type Fold[U, F[_ <: U] <: U, Z <: U] = Z } final class Succ[N <: Nat] extends Nat { type Fold[U, F[_ <: U] <: U, Z <: U] = F[N#Fold[U, F, Z]] }

Page 31: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Type Lambdas (Scala 2.11)

Type-­‐Level  Programming  in  Scala   31  

def + (x: Nat) = fold[Nat]((n => new Succ(n)), x)

def + (x: Nat) = fold[Nat]((new { def l(n: Nat) = new Succ(n) }).l _, x)

type + [X <: Nat] = Fold[Nat, ({ type L[N <: Nat] = Succ[N] })#L, X]

Page 32: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Type Lambdas (Experimental)

Type-­‐Level  Programming  in  Scala   32  

def + (x: Nat) = fold[Nat]((n => new Succ(n)), x)

type + [X <: Nat] = Fold[Nat, [N <: Nat] => Succ[N], X]

Page 33: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Limits of Recursion

Type-­‐Level  Programming  in  Scala   33  

sealed trait Nat { type Switch[U, F[_ <: Nat] <: U, Z <: U] <: U } final object Zero extends Nat { type Switch[U, F[_ <: U] <: U, Z <: U] = Z } final class Succ[N <: Nat] extends Nat { type Switch[U, F[_ <: U] <: U, Z <: U] = F[N] }

type Fold[U, F[_ <: U] <: U, Z <: U] = Switch[U, ({ type L[P <: Nat] = P#Fold[U, F, Z] })#L, Z]

Not allowed!

Page 34: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Recursion

•  No recursive types allowed

•  But recursion through traits

•  Fixpoint combinators are possible (but ugly)

Type-­‐Level  Programming  in  Scala   34  

Page 35: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

"You'll Get Used To It In Time"

Type-­‐Level  Programming  in  Scala   35  

Page 36: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Combining Term- And Type-Level

Page 37: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Natural Numbers

Type-­‐Level  Programming  in  Scala   37  

object Zero extends Nat { type This = Zero.type type Fold[U, F[_ <: U] <: U, Z <: U] = Z def value = 0 } class Succ[N <: Nat](val value: Int) extends Nat { type This = Succ[N] type Fold[U, F[_ <: U] <: U, Z <: U] = F[N#Fold[U, F, Z]] }

Page 38: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Hybrid operation: Term- & type-level

Efficient Natural Numbers

Type-­‐Level  Programming  in  Scala   38  

sealed trait Nat { def value: Int // This, Fold, equals, hashCode, toString... type + [N <: Nat] = Fold[Nat, ({ type L[X <: Nat] = Succ[X] })#L, N] }

def + [T <: Nat](n: T): +[T] = Nat._unsafe[+[T]](value + n.value)

object Nat { def _unsafe[T <: Nat](v: Int) = ( if(v == 0) Zero else new Succ(v) ).asInstanceOf[T] }

Page 39: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Heterogeneous Lists

Page 40: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

HList Basics

Type-­‐Level  Programming  in  Scala   40  

sealed abstract class HList { type This >: this.type <: HList type Head type Tail <: HList def head: Head def tail: Tail } final class HCons[H, T <: HList](val head: H, val tail: T) extends HList { type This = HCons[H, T] type Head = H type Tail = T } object HNil extends HList { ... }

Page 41: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

HList Basics

Type-­‐Level  Programming  in  Scala   41  

sealed abstract class HList { type This >: this.type <: HList type Head type Tail <: HList def head: Head def tail: Tail } object HNil extends HList { type This = HNil.type type Head = Nothing type Tail = Nothing def head = throw new NoSuchElementException("HNil.head") def tail = throw new NoSuchElementException("HNil.tail") }

Cannot fail to compile

Page 42: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

HList Length

Type-­‐Level  Programming  in  Scala   42  

sealed abstract class HList { def isDefined: Boolean == this != HNil def size: Int = { var i = 0 var h = this while(h.isDefined) { i += 1; h = h.tail } i } type Length = }

Fold[Nat, ({ type L[_, Z <: Nat] = Z# ++ })#L, Zero] def length: Length = Nat._unsafe[Length](size)

Page 43: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

HList Fold (Type-Level)

Type-­‐Level  Programming  in  Scala   43  

sealed abstract class HList { type Fold[U, F[_, _ <: U] <: U, Z <: U] <: U } final class HCons[H, T <: HList](val head: H, val tail: T) extends HList { type Fold[U, F[_, _ <: U] <: U, Z <: U] = F[Head, T#Fold[U, F, Z]] } object HNil extends HList { type Fold[U, F[_, _ <: U] <: U, Z <: U] = Z }

Page 44: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

HList Fold (Term-Level)

Type-­‐Level  Programming  in  Scala   44  

sealed abstract class HList { def fold[U](f: (Any, U) => U, z: => U): U } final class HCons[H, T <: HList](val head: H, val tail: T) extends HList { def fold[U](f: (Any, U) => U, z: => U) = f(head, tail.fold[U](f, z)) } object HNil extends HList { def fold[U] (f: (Any, U) => U, z: => U) = z }

Function2

Page 45: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Hybrid Functions

Type-­‐Level  Programming  in  Scala   45  

trait Function2[-T1, -T2, +R] { def apply(v1: T1, v2: T2): R }

trait TypedFunction2[-T1, -T2, +R, F[_ <: T1, _ <: T2] <: R] { def apply[P1 <: T1, P2 <: T2](p1: P1, p2: P2): F[P1, P2] }

Page 46: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

HList Fold (Hybrid)

Type-­‐Level  Programming  in  Scala   46  

sealed abstract class HList { def fold[U, F[_, _ <: U] <: U, Z <: U] (f: TypedFunction2[Any, U, U, F], z: Z): Fold[U, F, Z] } final class HCons[H, T <: HList](val head: H, val tail: T) extends HList { def fold[U, F[_, _ <: U] <: U, Z <: U] (f: TypedFunction2[Any, U, U, F], z: Z): Fold[U, F, Z] = f.apply[Head, T#Fold[U, F, Z]] (head, tail.fold[U, F, Z](f, z)) } object HNil extends HList { def fold[U, F[_, _ <: U] <: U, Z <: U] (f: TypedFunction2[Any, U, U, F], z: Z) = z }

Page 47: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

HList Length With Fold

Type-­‐Level  Programming  in  Scala   47  

sealed abstract class HList { def length: Length = fold[ Nat, ({ type L[_, Z <: Nat] = Z# ++ })#L, Zero.type ]( new TypedFunction2[Any, Nat, Nat, ({ type L[_, Z <: Nat] = Z# ++ })#L] { def apply[P1 <: Any, P2 <: Nat](p1: P1, p2: P2) = p2.++ }, Zero ) }

Page 48: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Limitations

•  No recursive types (but recursion through traits)

•  Type lambdas and typed functions are cumbersome

•  The type language is total

•  No unification

Type-­‐Level  Programming  in  Scala   48  

Page 49: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

Time For Some Tea

Type-­‐Level  Programming  in  Scala   49  

Page 50: Type-Level Computations in Scala · Type%Level)Programming)in)Scala 40 sealed abstract class HList { type This >: this.type

©Typesafe 2014 – All Rights Reserved

@StefanZeiger  

hCps://github.com/szeiger/ErasedTypes