Julien Richard-Foy
Typelevel Summit Copenhagen – June 3, 2017
http://julienrf.github.io/2017/arrows
<html>
<head><title>This is not a title</title></head>
<body/>
</html>
1 + 1 // This is not an addition
Description | Interpretations |
---|---|
HTML document | rendering |
arithmetic expression | evaluation, simplification, pretty print |
image | draw on screen, generate a file |
API endpoint | client, server, documentation |
1
, "foo"
, point(1, 2)
)+
, concat
, move
)trait DataDescr {
/** A description of data of type `A` */
type Data[A]
/** “atom” describing a record with one
* field of type `String`
*/
def field(name: String): Data[String]
}
DataDescr
languagetrait Program extends DataDescr {
/**
* A record with one field named “x”
* and containing a `String` value
*/
val x: Data[String] = field("x")
}
User
data type (1)trait Program extends DataDescr {
case class User(name: String, email: String)
}
User
data type (2)trait Program extends DataDescr {
case class User(name: String, email: String)
val user: Data[User] = {
???
}
}
User
data type (3)trait Program extends DataDescr {
case class User(name: String, email: String)
val user: Data[User] = {
val name = field("name")
???
}
}
User
data type (4)trait Program extends DataDescr {
case class User(name: String, email: String)
val user: Data[User] = {
val name = field("name")
val email = field("email")
???
}
}
import scalaz.Applicative
trait DataDescr {
type Data[A]
def field(name: String): Data[String]
/** Pretend that `Data[_]` is an applicative functor */
implicit val applicativeData: Applicative[Data]
}
import scalaz.syntax._
trait Program extends DataDescr {
case class User(name: String, email: String)
val user: Data[User] = {
val name = field("name")
val email = field("email")
(name tuple email) // Data[(String, String)]
}
}
import scalaz.syntax._
trait Program extends DataDescr {
case class User(name: String, email: String)
val user: Data[User] = {
val name = field("name")
val email = field("email")
(name tuple email).map(User.tupled)
}
}
val name: Data[String] = …
val email: Data[String] = …
val user: Data[User] = (name tuple email).map(User.tupled)
Data[User]
?Data[User]
?Data
is still an abstract typeData
a concrete meaningDecoder
trait Decoder extends DataDescr {
type Data[A] = Map[String, String] => Option[A]
// … (implementation of `field` and `applicativeData`)
}
Documentation
trait Documentation extends DataDescr {
type Data[A] = Record
case class Record(fields: List[String])
// … (implementation of `field` and `applicativeData`)
}
sealed trait Shape
case class Circle(radius: String) extends Shape
case class Rectangle(width: String, height: String) extends Shape
val kvs =
Map(
"type" -> "Circle",
"radius" -> "42"
)
sealed trait Shape
case class Circle(radius: String) extends Shape
case class Rectangle(width: String, height: String) extends Shape
val shape: Data[Shape] = {
val tpe = field("type")
val circle = field("radius").map(Circle)
val rectangle =
(field("width") tuple field("height")).map(Rectangle.tupled)
???
}
import scalaz.Monad
trait DataDescr {
type Data[A]
def field(name: String): Data[String]
/** Pretend that `Data[_]` is a monad */
implicit val monadData: Monad[Data]
}
val shapeData: Data[Shape] = {
val circle: Data[Shape] = field("radius").map(Circle)
val rectangle: Data[Shape] =
(field("width") tuple field("height")).map(Rectangle.tupled)
for {
tpe <- field("type")
shape <- tpe match {
case "Circle" => circle
case "Rectangle" => rectangle
}
} yield shape
}
Decoder
trait Decoder extends DataDescr {
type Data[A] = Map[String, String] => Option[A]
// … (implementation of `monadData`)
}
Documentation
trait Documentation extends DataDescr {
type Data[A] = Record
case class Record(fields: List[String])
val monadData: Monad[Data] = ???
}
Documentation
trait Documentation extends DataDescr {
type Data[A] = Record
case class Record(fields: List[String])
val monadData: Monad[Data] =
new Monad[Data] {
def point[A](x: A): Data[A] = ???
def bind[A, B](fa: Data[A])(f: A => Data[B]): Data[B] = ???
}
}
Documentation
trait Documentation extends DataDescr {
type Data[A] = Record
case class Record(fields: List[String])
val monadData: Monad[Data] =
new Monad[Data] {
def point[A](x: A): Record = ???
def bind[A, B](fa: Record)(f: A => Record): Record = ???
}
}
Documentation
trait Documentation extends DataDescr {
type Data[A] = Record
case class Record(fields: List[String])
val monadData: Monad[Data] =
new Monad[Data] {
def point[A](x: A): Record = Record(Nil)
def bind[A, B](fa: Record)(f: A => Record): Record = ???
}
}
Documentation
trait Documentation extends DataDescr {
type Data[A] = Record
case class Record(fields: List[String])
val monadData: Monad[Data] =
new Monad[Data] {
def point[A](x: A): Record = Record(Nil)
def bind[A, B](fa: Record)(f: A => Record): Record = fa
}
}
Documentation
Documentation
implementation…Data
an arrowtrait DataDescr {
/** Description of a data type */
type Data[In, Out]
/** Raw format of data */
type Raw
/** Describes a record with one field */
def field(name: String): Data[Raw, String]
/** Pretend that `Data[_, _]` is an arrow */
implicit val arrowData: Arrow[Data]
}
import scalaz.syntax.all._
trait Program exends DataDescr {
import arrowData._
case class User(name: String, email: String)
def userData: Data[Raw, User] = {
val name = field("name")
val email = field("email")
(name &&& email) // Data[Raw, (String, String)]
}
}
import scalaz.syntax.all._
trait Program exends DataDescr {
import arrowData._
case class User(name: String, email: String)
def userData: Data[Raw, User] = {
val name = field("name")
val email = field("email")
(name &&& email) >>> arr(User.tupled)
}
}
>>>
) or in parallel (&&&
)sealed trait Shape
case class Circle(radius: String) extends Shape
case class Rectangle(width: String, height: String) extends Shape
val shapeDecoder: Data[Raw, Shape] = {
val circle: Data[Raw, Shape] = field("radius") >>> arr(Circle)
val rectangle: Data[Raw, Shape] =
(field("width") &&& field("height")) >>> arr(Rectangle.tupled)
val tpe = field("type")
???
}
trait DataDescr {
/** Pretend that `Data[_, _]` is an arrow with choice */
implicit val arrowChoiceData: Arrow[Data] with Choice[Data]
}
val shapeDecoder: Data[Raw, Shape] = {
import arrowChoiceData._
val circle: Data[Raw, Shape] = …
val rectangle: Data[Raw, Shape] = …
val tpe: Data[Raw, Unit \/ Unit] =
field("type") >>> arr((_: String) match {
case "Circle" => ().left
case "Rectangle" => ().right
}
tpe.withFst >>> (circle ||| rectangle)
}
shapeDecoder
Documentation
trait Documentation extends DataDescr {
type Data[A, B] = ???
type Raw = ???
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] = ???
}
Documentation
trait Documentation extends DataDescr {
type Data[A, B] = ???
type Raw = Nothing
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] = ???
}
Documentation
trait Documentation extends DataDescr {
type Data[A, B] = ???
type Raw = Nothing
case class Record(fields: List[String])
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] = ???
}
Documentation
trait Documentation extends DataDescr {
type Data[A, B] = ???
type Raw = Nothing
sealed trait Adt
case class Record(fields: List[String]) extends Adt
case class CoProd(alternatives: List[Record]) extends Adt
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] = ???
}
Documentation
trait Documentation extends DataDescr {
type Data[A, B] = Adt
type Raw = Nothing
sealed trait Adt
case class Record(fields: List[String]) extends Adt
case class CoProd(alternatives: List[Record]) extends Adt
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] = ???
}
Documentation
sealed trait Adt
case class Record(fields: List[String]) extends Adt
case class CoProd(alternatives: List[Record]) extends Adt
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] =
new Arrow[Data] with Choice[Data] {
// …
}
Documentation
sealed trait Adt
case class Record(fields: List[String]) extends Adt
case class CoProd(alternatives: List[Record]) extends Adt
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] =
new Arrow[Data] with Choice[Data] {
def compose[A, B, C](f: Data[B, C], g: Data[A, B]): Data[A, C] = ???
// …
}
Documentation
sealed trait Adt
case class Record(fields: List[String]) extends Adt
case class CoProd(alternatives: List[Record]) extends Adt
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] =
new Arrow[Data] with Choice[Data] {
def compose[A, B, C](f: Adt, g: Adt): Adt = ???
// …
}
Documentation
sealed trait Adt
case class Record(fields: List[String]) extends Adt
case class CoProd(alternatives: List[Record]) extends Adt
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] =
new Arrow[Data] with Choice[Data] {
def compose[A, B, C](f: Adt, g: Adt): Adt =
(f, g) match {
case (Record(fs1), Record(fs2)) => Record(fs1 ++ fs2)
}
// …
}
Documentation
sealed trait Adt
case class Record(fields: List[String]) extends Adt
case class CoProd(alternatives: List[Record]) extends Adt
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] =
new Arrow[Data] with Choice[Data] {
def compose[A, B, C](f: Adt, g: Adt): Adt =
(f, g) match {
case (Record(fs1), Record(fs2)) => Record(fs1 ++ fs2)
case (CoProd(as1), CoProd(as2)) => CoProd(as1 ++ as2)
}
// …
}
Documentation
sealed trait Adt
case class Record(fields: List[String]) extends Adt
case class CoProd(alternatives: List[Record]) extends Adt
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] =
new Arrow[Data] with Choice[Data] {
def compose[A, B, C](f: Adt, g: Adt): Adt =
(f, g) match {
case (Record(fs1), Record(fs2)) => Record(fs1 ++ fs2)
case (CoProd(as1), CoProd(as2)) => CoProd(as1 ++ as2)
case (Record(fs1), CoProd(as2)) => CoProd(as2.map(r => Fields(r.fields ++ fs1)))
case (CoProd(as1), Record(fs2)) => coProd(as1.map(r => Fields(r.fields ++ fs2)))
}
// …
}
Documentation
sealed trait Adt
case class Record(fields: List[String]) extends Adt
case class CoProd(alternatives: List[Record]) extends Adt
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] =
new Arrow[Data] with Choice[Data] {
def choice[A, B, C](f: Data[A, C], g: Data[B, C]): Data[A \/ B, C] = ???
// …
}
Documentation
sealed trait Adt
case class Record(fields: List[String]) extends Adt
case class CoProd(alternatives: List[Record]) extends Adt
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] =
new Arrow[Data] with Choice[Data] {
def choice[A, B, C](f: Adt, g: Adt): Adt = ???
// …
}
Documentation
sealed trait Adt
case class Record(fields: List[String]) extends Adt
case class CoProd(alternatives: List[Record]) extends Adt
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] =
new Arrow[Data] with Choice[Data] {
def choice[A, B, C](f: Adt, g: Adt): Adt =
(f, g) match {
case (r1: Record, r2: Record) => CoProd(r1 :: r2 :: Nil)
}
// …
}
Documentation
sealed trait Adt
case class Record(fields: List[String]) extends Adt
case class CoProd(alternatives: List[Record]) extends Adt
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] =
new Arrow[Data] with Choice[Data] {
def choice[A, B, C](f: Adt, g: Adt): Adt =
(f, g) match {
case (r1: Record, r2: Record) => CoProd(r1 :: r2 :: Nil)
case (CoProd(as1), CoProd(as2)) => CoProd(as1 ++ as2)
}
// …
}
Documentation
sealed trait Adt
case class Record(fields: List[String]) extends Adt
case class CoProd(alternatives: List[Record]) extends Adt
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] =
new Arrow[Data] with Choice[Data] {
def choice[A, B, C](f: Adt, g: Adt): Adt =
(f, g) match {
case (r1: Record, r2: Record) => CoProd(r1 :: r2 :: Nil)
case (CoProd(as1), CoProd(as2)) => CoProd(as1 ++ as2)
case (r1: Record, CoProd(as2)) => CoProd(r1 :: as2)
}
// …
}
Documentation
sealed trait Adt
case class Record(fields: List[String]) extends Adt
case class CoProd(alternatives: List[Record]) extends Adt
implicit val arrowChoiceData: Arrow[Data] with Choice[Data] =
new Arrow[Data] with Choice[Data] {
def choice[A, B, C](f: Adt, g: Adt): Adt =
(f, g) match {
case (r1: Record, r2: Record) => CoProd(r1 :: r2 :: Nil)
case (CoProd(as1), CoProd(as2)) => CoProd(as1 ++ as2)
case (r1: Record, CoProd(as2)) => CoProd(r1 :: as2)
case (CoProd(as1), r2: Record) => CoProd(r2 :: as1)
}
// …
}