On Github NickPollard / typing
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
Overloading Typeclassestrait Show[T] { def show(t: T): String }
def f[T](...)(implicit ev: Show[T])
def f[T : Show](...)Instances are defined as implicit values
implicit val showInt : Show[Int] = new Show[Int] { def show(i: Int): String = i.toString }Can construct through implicit defs
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
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