High Wizardry in the Land of Scala
-
Upload
djspiewak -
Category
Technology
-
view
3.730 -
download
2
Transcript of High Wizardry in the Land of Scala
High Wizardry in the Land of ScalaDaniel Spiewak
Agenda
• Higher-Kinds
• Typeclasses
• Type-Level Encodings
• Continuations
Higher-Kinds
• What is a “kind system”?
Higher-Kinds
• What is a “kind system”?
• What is a “type system”?
A type system is a tractable syntactic method for proving the absence of certain program behaviors by classifying phrases according to the kinds of
values they compute. – Benjamin Pierce
val i: Int = 42val j: Int = 21
val s: String = "foo"
val f: Int => String = { _.toString }
val xs: List[Int] = List(1, 1, 2, 3, 5, 8)
Values
Values
Types
Values
Types
???
Values
Types
Kinds
Higher-Kinds
• Type systems classify values
• Kind systems classify types
• Values are to types as types are to kinds
type Int :: *
type String :: *
type (Int => String) :: *
type List[Int] :: *
type List :: ???
type Function1 :: ???
type List :: * => *
type Function1 :: (* × *) => *
// id : Int => Intdef id(x: Int) = x
// Id :: * => *type Id[A] = A
// id : ((Int => Int), Int) => Intdef id(f: Int => Int, x: Int) = f(x)
// Id :: ((* => *) × *) => *type Id[A[_], B] = A[B]
val map: Map[Option[Any], List[Any]] = Map( Some("foo") -> List("foo", "bar", "baz"), Some(42) -> List(1, 1, 2, 3, 5, 8), Some(true) -> List(true, false, true, true))
// ugly cast!val xs: List[String] = map(Some("foo")).asInstanceOf[List[String]]
// ditto!val ys: List[Int] = map(Some(42)).asInstanceOf[List[Int]]
val map: HOMap[Option, List] = HOMap[Option, List]( Some("foo") -> List("foo", "bar", "baz"), Some(42) -> List(1, 1, 2, 3, 5, 8), Some(true) -> List(true, false, true, true))
// blissful type safety!val xs: List[String] = map(Some("foo"))
// ditto!val ys: List[Int] = map(Some(42))
// HOMap :: ((* => *) × (* => *)) => *class HOMap[K[_], V[_]](delegate: Map[K[Any], V[Any]]) { def apply[A](key: K[A]): V[A] = delegate(key.asInstanceOf[K[Any]]).asInstanceOf[V[A]]}
object HOMap { def apply[K[_], V[_]](tuples: (K[Any], V[Any])*) = new HOMap[K, V](Map(tuples: _*))}
(credit: Jorge Ortiz)
Higher-Kinds
• Kind systems classify types
• Values are to types as types are to kinds
• “Higher” kinds are the kinds of type constructors
• Type functions
• Use any time one type is logically a function of another
Typeclasses
• Forget everything you know about classes
• (it won’t help you anyway)
• Instead of “class”, think “category”
• If you’ve ever looked at Haskell…
sum(List(1, 2, 3, 4)) // => 10sum(List(3.14, 2.72)) // => 5.86sum(List("me", "you")) // shouldn't compile!
trait Num[A] { val zero: A
def add(x: A, y: A): A}
def sum[A](nums: List[A])(tc: Num[A]) = nums.foldLeft(tc.zero)(tc.add)
object IntNum extends Num[Int] { val zero = 0
def add(x: Int, y: Int) = x + y}
object DoubleNum extends Num[Double] { val zero = 0d
def add(x: Double, y: Double) = x + y}
// works!sum(List(1, 2, 3, 4))(IntNum)sum(List(3.14, 2.72))(DoubleNum)
Typeclasses
• This is functional, but ugly
• We have to explicitly provide the relevant instance of Num[A]
Typeclasses
• This is functional, but ugly
• We have to explicitly provide the relevant instance of Num[A]
def sum[A](nums: Seq[A])(tc: Num[A]) = nums.foldLeft(tc.zero)(tc.add)
def sum[A](nums: Seq[A])(implicit tc: Num[A]) = nums.foldLeft(tc.zero)(tc.add)
object IntNum extends Num[Int] { val zero = 0
def add(x: Int, y: Int) = x + y}
object DoubleNum extends Num[Double] { val zero = 0d
def add(x: Double, y: Double) = x + y}
implicit object IntNum extends Num[Int] { val zero = 0
def add(x: Int, y: Int) = x + y}
implicit object DoubleNum extends Num[Double] { val zero = 0d
def add(x: Double, y: Double) = x + y}
sum(List(1, 2, 3, 4))(IntNum)sum(List(3.14, 2.72))(DoubleNum)
sum(List(1, 2, 3, 4))sum(List(3.14, 2.72))
Typeclasses
• Typeclasses are categories of types
• If you have a set of types with well-defined commonalities, think about typeclasses
• Collections in 2.8
• Numeric in 2.8
Type-Level Encodings
• Kinds make our types into superheroes
• Typeclasses allow us to abstract over types
• How can we abuse our new-found power?
Type-Level Encodings
• Kinds make our types into superheroes
• Typeclasses allow us to abstract over types
• How can we abuse our new-found power?
• Maybe…data structures at the type level?
Type-Level Encodings
• HList is a linked-list implemented in types
• …and values
• Sort of like Tuple, but unbounded
import HList._
val xs = 42 :: "foo" :: 3.14 :: HNil
xs.head // => 42: Intxs.tail.head // => "foo": String
val xs1 = 42 :: false :: HNilval xs2 = "Hello" :: "World" :: HNil
val xs = xs1 ++ xs2
xs.head // => 42: Intxs.tail.tail.head // => "Hello": String
object HList { sealed trait HList { type Head type Tail <: HList type Append[L <: HList] <: HList def head: Head def tail: Tail def ++[L <: HList](xs: L): Append[L] } // ...}
x
y
val x: List[Int] = ...val y: List[Int] = ...
x ++ y
1 2 3 4
5 6 7 8 9
x
y
val x: List[Int] = ...val y: List[Int] = ...
x ++ y
2 3 4
5 6 7 8 9
x
y
val x: List[Int] = ...val y: List[Int] = ...
x ++ y
3 4
5 6 7 8 9
x
y
val x: List[Int] = ...val y: List[Int] = ...
x ++ y
4
5 6 7 8 9
y
val x: List[Int] = ...val y: List[Int] = ...
x ++ y
5 6 7 8 9
x’
y
val x: List[Int] = ...val y: List[Int] = ...
x ++ y
5 6 7 8 9
4x’
y
val x: List[Int] = ...val y: List[Int] = ...
x ++ y
3
5 6 7 8 9
4x’
y
val x: List[Int] = ...val y: List[Int] = ...
x ++ y
2 3
5 6 7 8 9
4x’
y
val x: List[Int] = ...val y: List[Int] = ...
x ++ y
1 2 3
5 6 7 8 9
4x’
object HList { // ... final class HNil extends HList { type Head = Nothing type Tail = Nothing type Append[L <: HList] = L def head = error("Head of an empty HList") def tail = error("Tail of an empty HList") def ::[A](a: A) = HCons(a, this) def ++[L <: HList](xs: L) = xs } val HNil = new HNil}
object HList { // ... case class HCons[A, B <: HList](head: A, tail: B) extends HList {
type Head = A type Tail = B
type Append[L <: HList] = HCons[Head, Tail#Append[L]] def ::[C](c: C) = HCons(c, this) def ++[L <: HList](xs: L) = head :: (tail ++ xs) } type ::[A, B <: HList] = HCons[A, B]}
Type-Level Encodings
• What about an nth(Int) function?
Type-Level Encodings
• What about an nth(Int) function?
• Not today!
• Church Numerals
• λ-Calculus
Type-Level Encodings
• What about an nth(Int) function?
• Not today!
• Church Numerals
• λ-Calculus
• We could do a lot more
• Just not in a 45 minute talk
Continuations
• Actually, delimited continuations
• Very different from plain continuations!
• Not like callcc
• Not considered harmful
• …though they can simulate goto!
case class JumpException(i: Int) extends RuntimeException
val res = try { val i = 42 println("before") throw JumpException(i) // basically: `break`
val j: Int = i / 2 println("after") println(j + 2) j // needed for type checker} catch { case JumpException(i) => i}
println("outside")
val (res, func) = { val i = 42 println("before") (i, { j: Int => println("after") println(j + 2) })}
println("outside")func(res / 2)func(res / 6)
val (res, func) = reset { val i = 42 println("before") val j = shift { (k: Int => Unit) => (i, k) } println("after") println(j + 2)}
println("outside")func(res / 2)func(res / 6)
val (res, func) = reset { val i = 42 println("before") val j = shift { (k: Int => Unit) => (i, k) } println("after") println(j + 2)}
println("outside")func(res / 2)func(res / 6)
val (res, func) = reset { val i = 42 println("before") val j = shift { (k: Int => Unit) => (i, k) } println("after") println(j + 2)}
println("outside")func(res / 2)func(res / 6)
val (res, func) = reset { val i = 42 println("before") val j = shift { (k: Int => Unit) => (i, k) } println("after") println(j + 2)}
println("outside")func(res / 2)func(res / 6)
def gen() = { var x = 1 var y = 1 while (true) { shift { (k: Unit => Result) => Result(x, k) } y += x x = y - x }}
val res = reset { gen() error("It never ends that way, too!"): Result}
val fib: Stream[Int] = res.toStream
(credit: PEP-255)
def gen() = { var x = 1 var y = 1 while (true) { shift { (k: Unit => Result) => Result(x, k) } y += x x = y - x }}
val res = reset { gen() error("It never ends that way, too!"): Result}
val fib: Stream[Int] = res.toStream
(credit: PEP-255)
Continuations
• This is cool and all, but what’s it good for?
Continuations
• This is cool and all, but what’s it good for?
• Not as much as you would think
reset { for (i <- 0 to 10) { shift { (k: Unit => Unit) => i } }}
reset { for (i <- 0 to 10) { shift { (k: Unit => Unit) => i } }}
Continuations
• This is cool and all, but what’s it good for?
• Not as much as you would think
• Nonblocking I/O
• Multi-page wizards
• Framework support is needed
Conclusion
• Higher-Kinds
• Classify types
• Typeclasses
• Categorize types
• Type Encodings
• Are really cool!
• Continuations
• Powerful
• ...but useless
Questions?