Motivation and Mechanics behind some aspects of
Shapeless
Plan
•Motivation•HList•Coproduct•Generic•Example
Motivation
Theory: product, coproduct and type classes
• Product: a and b.
• Coproduct: a or b.
• Type class: operations possible on a type.
type Phone = (Manufacturer, Model, Imei)
sealed trait Device
trait Phone extends Device
trait Laptop extends Device
trait ShowCsv[A] {
def showCsv(a: A): String
}
Why Shapeless?
•Manipulation of the Scala data types (e.g. treat a case class as a list).
•Type-level representations of data structures and operations over them.
HList: Generalisation of the product type
HList: Implementation
sealed trait HList
case class ::[+H, +T <: HList](head : H, tail : T) extends HList
case object HNil extends HList
HList: Example – Head of an Empty Listscala> List(1).head
res0: Int = 1
scala> Nil.head
java.util.NoSuchElementException: head of empty list
at scala.collection.immutable.Nil$.head(List.scala:420)
... 42 elided
scala> (1 :: HNil).head
res2: Int = 1
scala> HNil.head
<console>:15: error: could not find implicit value for parameter
c: shapeless.ops.hlist.IsHCons[shapeless.HNil.type]
HNil.head
Coproduct
Coproduct: Implementation
trait Coproduct
trait :+:[+H, +T <: Coproduct] extends
case class Inl[+H, +T <: Coproduct](head : H) extends :+:[H, T]
case class Inr[+H, +T <: Coproduct](tail : T) extends :+:[H, T]
trait CNil extends Coproduct
Shapeless Operations
Shapeless Operations
•Data types (HList, Coproduct etc) reside under the shapeless package.•Operations on these data types are provided by type
classes under shapeless.ops package.•Syntax available via rich wrappers under the shapeless.syntax package.•Companion objects of data types provide implicit
conversions to the rich wrappers.•Rich wrappers delegate to operation type classes.
Shapeless Operations: HList companion
object HList {
import syntax.HListOps
implicit def hlistOps[L <: HList](l: L): HListOps[L] = new HListOps(l)
}
Shapeless Operations: HList Syntax
final class HListOps[L <: HList](l : L) {
def head(implicit c: IsHCons[L]): c.H = c.head(l)
def tail(implicit c: IsHCons[L]): c.T = c.tail(l
}
Shapeless Operations: HList Type classes
object hlist {
trait IsHCons[L <: HList] {
type H
type T <: HList
def head(l: L): H
def tail(l: L): T
def cons(h: H, t: T): L
}
object IsHCons { /*...*/ }
}
Generic
Generic – Motivation
•HLists and Coproducts are another way to express case classes and supertypes.•They provide lost of useful collections operations.•Can we apply these operations to case classes or
supertypes?•Can we define operations applicable to all case
classes or supertypes? E.g. convert any case class to CSV string without defining the logic for each case class?
Generic: Implementationtrait Generic[T] {
type Repr
def to(t : T) : Repr
def from(r : Repr) : T
}
object Generic {
type Aux[T, Repr0] = Generic[T] { type Repr = Repr0 }
def apply[T](implicit gen: Generic[T]): Aux[T, gen.Repr] = gen
implicit def materialize[T, R]: Aux[T, R] =
macro GenericMacros.materialize[T, R]
}
Live Example: Converting an electronic store’s database to
CSV
Q&A