Using type information to generate behaviour
Nick Pollard
Scotiabank
Scala has a powerful static type system
What is it good for?
We use types to drive behaviour all the time
trait Show[T] {
def show(t: T): String
}
def f[T](...)(implicit ev: Show[T])
def f[T : Show](...)
implicit val showInt : Show[Int] = new Show[Int] {
def show(i: Int): String = i.toString
}
implicit def list[T](implicit s: Show[T]): Show[List[T]] =
new Show[List[T]] {
def show(ts: List[T]): String =
ts.map(s.show).mkString("(", ",", ")")
}
When searching for an implicit, the compiler can chain successive implicit functions if it will produce the required type
Objects | Operators |
numbers | +-*/ |
types | type operators |
What does it mean to operate on types?
What is a type?
We can view types as sets of values
Bool | Set(false, true) |
Int | Set(-2,147,483,648, -2,147,483,647, ... -1, 0, 1, 2 ...) |
Char | Set('a', 'b', 'c', 'd', ... ) |
Now we can just steal set operators!
We can express types in terms of operators
sealed trait HList
case class ::[H,T <: HList](head: H, tail: T) extends HList
case object HNil extends HList
type Example = Int :: String :: Double :: HNil
type Example = ::[Int,::[String,::[Double,HNil]]]
sealed trait Coproduct
sealed trait :+:[H,T <: Coproduct] extends Coproduct
sealed trait CNil extends Coproduct
type Example = Int :+: String :+: Double :+: HNil
type Example = :+:[Int,:+:[String,:+:[Double,HNil]]]
trait Parsable[T] {
def parser(open: String, sep: String, close: String) : Parser[T]
}
We would like to be able to define a simple type hierarchy, and automatically parse that
implicit def hnil: Parsable[HNil] = const[HNil](Pass map (_ => HNil))
implicit def hcons[K <: Symbol, H, T <: HList](implicit
head: Lazy[Parsable[H]],
tail: Lazy[Parsable[T]]
): Parsable[FieldType[K,H] :: T] =
new Parsable[FieldType[K,H] :: T] {
def parser(open: String, sep: String, close: String) =
head.value.parser(open, sep, close) ~ P(sep) ~
tail.value.parser(open, sep, close) map {
case (h,t) => field[K](h) :: t
}
}
implicit def cnil : Parsable[CNil] = const[CNil](Fail)
implicit def ccons[K <: Symbol, H, T <: Coproduct](implicit
key: Witness.Aux[K] ,
head: Lazy[Parsable[H]],
tail: Lazy[Parsable[T]]
): Parsable[FieldType[K,H] :+: T] =
new Parsable[FieldType[K,H] :+: T] {
def parser(open: String, sep: String, close: String) =
P(P(key.value.name) ~ P(open) ~
head.value.parser(open, sep, close).map(l => Inl(field[K](l))) ~
P(close)) |
P(tail.value.parser(open, sep, close).map(Inr(_)))
}
implicit def project[T,U](implicit ev: LabelledGeneric.Aux[T,U],
p: Lazy[Parsable[U]]) : Parsable[T] =
new Parsable[T] {
def parser(open: String, sep: String, close: String) : Parser[T] =
p.value.parser(open, sep, close) map ev.from
}
Define our data types
sealed trait Source
case class Http(url: String) extends Source
case class DB(table: String) extends Source
case class Fallback(first: Source, second: Source) extends Source
Summon a parser
val parsable = the[Parsable[Source]]
val parser = parsable.parser("(", ",", ")")
Use it!
val str = "Fallback(Http(\"http://www.example.com\"),DB(\"table\"))"
val result = parser.parse(str).get.value
assert(result === Fallback(Http("http://www.example.com"), DB("table")))
Shapeless by Miles Sabin
Fastparse by Li Haoyi
Scala eXchange by Skills Matter
This presentation will soon be available on https://skillsmatter.com/conferences/6862-scala-exchange-2015#skillscasts
Slides available at http://nickpollard.github.io/typing/
Presentation by Nick Pollard @ Scotiabank
nick.pollard@scotiabank.com
@nick_enGB