Julien Richard-Foy 
Scala Days – June 1st, 2017
http://julienrf.github.io/2017/existential-types
  
A problem well put is half-solved
John Dewey.
 
Breaking down problems into simpler problems
def identity[A](a: A): A| value | type | |
|---|---|---|
| parameter | ||
| abstract member | 
class Disc(radius: Double) {
  def area: Double = Pi * radius * radius
}def printArea(disc: Disc): Unit = println(disc.area)val disc = new Disc(123)
printArea(disc)| value | type | |
|---|---|---|
| parameter | (x: Int)(f: Int => Int) | |
| abstract member | 
abstract class Disc {
  def radius: Double
  def area: Double = Pi * radius * radius
}def printArea(disc: Disc): Unit = println(disc.area)val disc = new Disc { val radius = 123 }
printArea(disc)| value | type | |
|---|---|---|
| parameter | (x: Int)(f: Int => Int) | |
| abstract member | def x: Intdef f(y: Int): Int | 
trait Invertible[A] {
  def invert(a: A): A
}def invertLaw[T](t: T)(implicit invertible: Invertible[T]): Unit =
  assert(invertible.invert(invertible.invert(t)) == t)implicit val invertibleInt: Invertible[Int] =
  new Invertible[Int] { def invert(n: Int) = -n }
invertLaw(42)| value | type | |
|---|---|---|
| parameter | (x: Int)(f: Int => Int) | [A][F[_]] | 
| abstract member | def x: Intdef f(y: Int): Int | 
trait Invertible {
  type A
  def invert(a: A): A
}def invertLaw[T](t: T)(implicit invertible: Invertible { type A = T }): Unit =
  assert(invertible.invert(invertible.invert(t)) == t)implicit val invertibleInt: Invertible { type A = Int } =
  new Invertible { type A = Int; def invert(n: Int) = -n }
invertLaw(42)| value | type | |
|---|---|---|
| parameter | (x: Int)(f: Int => Int) | [A][F[_]] | 
| abstract member | def x: Intdef f(y: Int): Int | type Atype F[_] | 
object UnixFiles {
  def open(path: String): Int = …
  def read(file: Int): String = …
  def close(file: Int): Int = …
}def readSomething(): Unit = {
  UnixFiles.read(42) // I can forge a file descriptor!
}File typetrait UnixFiles {
  type File
  def open(path: String): File
  def read(file: File): String
  def close(file: File): Int
}def readSomething(files: UnixFiles): Unit = {
  files.read(42)
  //         ^^
  // Error: Type mismatch;
  //  found: Int
  //  required: files.File
}File typedef readSomething(files: UnixFiles): Unit = {
  val file = files.open("/foo")
  files.read(file) // I have no other choice than
                   // calling the `open` method to
                   // get a `File` instance
}object UnixFiles {
  sealed trait File
  private case class FileImpl(value: Int) extends File
  def open(path: String): File = {
    …
    FileImpl(…)
  }
  def read(file: File): String = file match {
    case FileImpl(fileImpl) => …
  }
}Component trait Component[State] {
  def view(state: State): Html
}Counter componentobject Counter extends Component[Int] {
  def view(state: Int): Html =
    <span>value = { state }</span>
  // … other methods defining how to update the state
}Container componentclass Container[State](child: Component[State]) extends Component[State] {
  
  def view(state: State): Html =
    <div class="container">{ child.view(state) }</div>
}Tabs component
 
Tabs componentclass Tabs(children: List[(String, Component[_]]))
  extends Component[Tabs.State[_]] {
  def view(state: Tabs.State[_]): Html = …
}
object Tabs {
  case class State[S](child: Component[S], childState: S)
}Tabs component implementationclass Tabs(children: List[(String, Component[_]]))
  extends Component[Tabs.State[_]] {
  def view(state: Tabs.State[_]): Html =
    <div class="tabs">
      <ul>{ for ((label, _) <- children) yield <li>{ label }</li> }</ul>
      <div>
        { state.child.view(state.childState) }
      </div>
    </div>
}Tabs component implementationclass Tabs(children: List[(String, Component[_]]))
  extends Component[Tabs.State[_]] {
  def view(state: Tabs.State[_]): Html =
    <div class="tabs">
      <ul>{ for ((label, _) <- children) yield <li>{ label }</li> }</ul>
      <div>
        { state.child.view(state.childState) }
//                         ^^^^^^^^^^^^^^^^
// type mismatch;
//  found   : state.childState.type (with underlying type _$1)
//  required: _$1
      </div>
    </div>
}Tabs component implementationclass Tabs(children: List[(String, Component[_]]))
  extends Component[Tabs.State[_]] {
  def view(state: Tabs.State[_]): Html = {
    def helper[A](s: Tabs.State[A]): Html =
      s.child.view(s.childState)
    <div class="tabs">
      <ul>{ for ((label, _) <- children) yield <li>{ label }</li> }</ul>
      <div>{ helper(state) }</div>
    </div>
  }
}Component with abstract type memberstrait Component {
  type State
  def view(state: State): Html
}Counter (again)object Counter extends Component {
  type State = Int
  def view(state: State): Html = <span>value = { state }</span>
  // … other methods defining how to update the state
  
}Container (again)trait Container extends Component {
  val child: Component
  type State = child.State
  def view(state: State): Html =
    <div class="container">{ child.view(state) }</div>
}Tabs – state (1)class Tabs(children: List[(String, Component])) extends Component {
  case class State(child: Component)(childState: child.State)
}State because of SI-5712Tabs – state (2)class Tabs(children: List[(String, Component])) extends Component {
  trait State {
    val child: Component
    def childState: child.State
  }
}Tabs – view implementationclass Tabs(children: List[(String, Component])) extends Component {
  def view(state: State): Html =
    <div class="tabs">
      <ul>{ for ((label, _) <- children) yield <li>{ label }</li> }</ul>
      <div>
        { state.child.view(state.childState) }
      </div>
    </div>
}
trait Plot {
  def setDataset(dataset: Dataset): Unit
  def getDataset: Dataset
}
trait DatasetXYPlot trait XYPlot extends Plot {
  private var _dataset: XYDataset = _
  def setDataset(dataset: Dataset) = _dataset = dataset.asInstanceOf[XYDataset]
  def getDataset = _dataset
}
case class XYDataset(series: List[Series]) extends Dataset
case class Series(values: List[(Double, Double)])trait Plot {
  type Dataset
  def setDataset(dataset: Dataset): Unit
  def getDataset: Dataset
}XYPlot (again) trait XYPlot extends Plot {
  case class Dataset(series: List[Series])
  case class Series(values: List[(Double, Double)])
  private var _dataset: Dataset = _
  def setDataset(dataset: Dataset) = _dataset = dataset
  def getDataset = _dataset
}sealed trait Expr
case class Lit(x: Double) extends Expr
case class Add(lhs: Expr, rhs: Expr) extends Exprval twoPlusThree = Add(Lit(2), Lit(3))Expr are descriptions of additionsdef eval(expr: Expr): Double =
  expr match {
    case Lit(x)        => x
    case Add(lhs, rhs) => eval(lhs) + eval(rhs)
  }def show(expr: Expr): String =
  expr match {
    case Lit(x)        => x.toString
    case Add(lhs, rhs) => print(lhs) ++ " + " ++ print(rhs)
  }def fold[A](lit: Double => A, add: (A, A) => A): Expr => A = {
  case Lit(x)        => lit(x)
  case Add(lhs, rhs) => add(lhs, rhs)
}val eval = fold[Double](identity, _ + _)
val show = fold[String](_.toString, _ ++ " + " ++ _)Expr values (“folds”) are interpretations of the languagetrait ExprDsl {
  type Expr
  def lit(x: Double): Expr
  def add(lhs: Expr, rhs: Expr): Expr
}trait Program extends ExprDsl {
  val twoPlusThree = add(lit(2), lit(3))
}trait ExprDsl {
  type Expr
  def lit(x: Double): Expr
  def add(lhs: Expr, rhs: Expr): Expr
}trait Eval extends ExprDsl {
  type Expr = Double
  def lit(x: Double) = x
  def add(lhs: Double, rhs: Double) = lhs + rhs
}new Program with Eval {
  println(twoPlusThree) // 5
}new Program with Show {
  println(twoPlusThree) // "2 + 3"
}See also [C. Hofer et al. 2008], [J. Carette et al. 2009] and [B. Oliveira 2012]
trait MulDsl extends ExprDsl {
  def mul(lhs: Expr, rhs: Expr)
}trait MulEval extends Mul with Eval {
  def mul(lhs: Double, rhs: Double) = lhs * rhs
}trait HttpEndpoints[Req, Resp, Headers, Url] {
  …
}trait Program[Req, Resp, Headers, Url]
  extends HttpEndpoints[Req, Resp, Headers, Url] {
  …
}trait HttpEndpoints {
  type Req
  type Resp
  type Headers
  type Url
  …
}trait Program extends HttpEndpointstrait HttpEndpoints {
  type Req <: ReqLike
  type Resp
  type Headers
  type Url
  …
}trait Program extends HttpEndpoints