recursive implicit proofs with shapeless hlists

36
Recursive implicit proofs with Shapeless HLists VLADIMIR PAVKIN RIGA SCALA MEETUP 18.08.2016 Introduction to:

Upload: vladimir-pavkin

Post on 08-Jan-2017

109 views

Category:

Software


5 download

TRANSCRIPT

Page 1: Recursive implicit proofs with Shapeless HLists

Recursive implicit proofswith Shapeless HLists

VLADIMIR PAVKINRIGA SCALA MEETUP

18.08.2016

Introduction to:

Page 2: Recursive implicit proofs with Shapeless HLists

Agenda1. Intro: another look at implicits2. Simple recursive proof with scala.math.Ordering3. Math proofs vs. Implicit-based proofs4. HList basics5. “Pure” proof for HList: OddProduct6. Show typeclass and it’s generic proof7. Expanding HList proof to all product-like types with shapeless.Generic8. Proofs with type-level computations: generic product diffs.

(given we have time)

Page 3: Recursive implicit proofs with Shapeless HLists

CODE SAMPLES

https://github.com/vpavkin/recursive-implicit-proofs-talk

Page 4: Recursive implicit proofs with Shapeless HLists

Another look at implicits

Page 5: Recursive implicit proofs with Shapeless HLists

Writing a program for a taskis very similar to

constructing a proof for a theorem.

Another look at implicits

Page 6: Recursive implicit proofs with Shapeless HLists

Compiler +

Type System=

Proof construction tool

Another look at implicits

Page 7: Recursive implicit proofs with Shapeless HLists

Typeclass – a property that can be proved

Another look at implicits

Implicit typeclass instance – a proved fact that some type has particular property

Ordering[A]

implicit val intOrdering = new Ordering[Int] {…}

Page 8: Recursive implicit proofs with Shapeless HLists

Implicit method parameter –a requirement that is proved by the compiler at every call site.

Another look at implicits

Implicit scope – the proof system. A set of axioms that is used by compiler to build more complex proofs.

Implicit not found – proof can’t be constructed.

def sortBy[B](f: A => B) (implicit ord: Ordering[B])

//or

def sortBy[B: Ordering](f: A => B)

implicit val intOrdering = …

implicit def optOrdering[T:Ordering] = new Ordering[Option[T]] { … }

// Okimplicitly[Ordering[Option[Int]]]

// Can’t proveimplicitly[Ordering[Option[String]]]

Page 9: Recursive implicit proofs with Shapeless HLists

Simple recursive proof withscala.math.Ordering

(Demo)

Page 10: Recursive implicit proofs with Shapeless HLists

Math proofs vs Scala implicit proofs

Page 11: Recursive implicit proofs with Shapeless HLists

Math proofs vs Scala implicit proofs

A // type A = Int

implicit val intOrdering: Ordering[Int] = …

Page 12: Recursive implicit proofs with Shapeless HLists

Math proofs vs Scala implicit proofs

A => B

// type B = Option[A]

implicit def optOrdering[A](implicit ev: Ordering[A]): Ordering[Option[A]] = …

Page 13: Recursive implicit proofs with Shapeless HLists

Math proofs vs Scala implicit proofs

A & B => C

// type C = (A, B)

implicit def tuple2Ordering[A, B](implicit A: Ordering[A], B: Ordering[B]): Ordering[(A, B)] = …

implicit def contravariantOrdering[A, B](implicit C: Converts[A, B], O: Ordering[B]): Ordering[A] =

Page 14: Recursive implicit proofs with Shapeless HLists

Math proofs vs Scala implicit proofs

A | B => Cimplicit def ordered[C <% Comparable[C]]: Ordering[C] = …

implicit def comparatorToOrdering[C](implicit cmp: Comparator[C]): Ordering[C] = …

A => C+

B => CAvoid ambiguous implicits!

Page 15: Recursive implicit proofs with Shapeless HLists

Math proofs vs Scala implicit proofs

!A !A => BA => !B

N/AYou can’t ask for an absence of implicit

Page 16: Recursive implicit proofs with Shapeless HLists

Math proofs vs Scala implicit proofs

Back to Ordering example1 -> Container(Option(Container("a")))

(Int, Container[Option[Container[String]]])

String Container[String] Option[Container[String]]

Container[Option[Container[String]]] (Int, Container[Option[Container[String]]])

A A => BOrd[T] => Ord[Container[T]]

A => BOrd[T] => Ord[Option[T]]

A => BOrd[T] => Ord[Container[T]]

A & B => COrd[A] & Ord[B] => Ord[(A,B)]

IntA

Page 17: Recursive implicit proofs with Shapeless HLists

HList basics

Page 18: Recursive implicit proofs with Shapeless HLists

HList basics

Scala List shapeless.HList

Root type List HList

Conscase class ::[A]( head: A, tail: List[A])

case class ::[H, T <: HList]( head: H, tail: T)

Nil Nil (singleton) HNil (singleton)

Type of 1 :: Nil ::[Int] Int :: HNil

Type of1 :: “a” :: Nil

::[Any] Int :: String :: HNil

Type of1 :: “a” :: true :: Nil

::[Any] Int :: String :: Boolean :: HNil

Page 19: Recursive implicit proofs with Shapeless HLists

Safe head/tail with precise types

• HList (and HNil) doesn’t define head/tail – you just can’t call.

• :: has them both, with precise types:• Int :: String :: HNil has:

• head: Int• tail: String :: HNil

HList basics

Page 20: Recursive implicit proofs with Shapeless HLists

Safe indexingfinal class HListOps[L <: HList](l : L) {

def at[N <: Nat](implicit at: At[L, N]): at.Out = at(l)}

HList basics

• Uses type-level natural numbers to build indexing proofs.• At[L, N] is one of that proofs we were talking before.• If implicit At is found – index is within list bounds. • If not – index out of range (implicit not found == can’t prove).• Result type is precise (not true for List).

Page 21: Recursive implicit proofs with Shapeless HLists

Isomorphic to product typescase class Person(name: String, age:Int) String :: Int :: HNil

(Int, Boolean) Int :: Boolean :: HNil

HList basics

• shapeless.Generic[T] typeclass provides conversions between product types’ values and HLists

• With the help of implicit macro, Generic instance is available for all tuples and case classes.

2nd most important property (along with keeping precise types):

Page 22: Recursive implicit proofs with Shapeless HLists

HList basics

(Demo)

Page 23: Recursive implicit proofs with Shapeless HLists

“Pure” HList proof: OddProduct

(Demo)

Page 24: Recursive implicit proofs with Shapeless HLists

Most of the time HList proofs

are inspired by Mathematical Induction

Page 25: Recursive implicit proofs with Shapeless HLists

“Pure” HList proof: OddProduct

OddProduct[Int :: String :: Boolean :: Int :: Int :: HNil]

Int :: HNil Boolean :: Int :: Int :: HNil

Int :: String:: Boolean :: Int :: Int :: HNil

A => BOdd[T <: HList] => Odd[H1 :: H2 :: T]

A => BOdd[T <: HList] => Odd[H1 :: H2 :: T]

AOdd[T :: HNil]

Recursive step – resolving to the same implicit def

Page 26: Recursive implicit proofs with Shapeless HLists

Show typeclass and it’s generic proof

(Demo)

Page 27: Recursive implicit proofs with Shapeless HLists

Expanding HList proof to all product-like types with

shapeless.Generic

Page 28: Recursive implicit proofs with Shapeless HLists

Generic.Aux[T, Repr] – Isomorphism, that allows us to get Repr from T and vice versa.Repr – some generic representation of T

Say we have typeclass Property[A].

If for some type T we have:Property[Repr] – our Property is proved for type Repr.&

We can (almost always) derive the Property[T] by converting to/from Repr and using Property[Repr]

Hint: Repr here is going to be an HList

Looks similar to A & B => C.

Let’s encode the proof!

Expanding HList proof to all product-like types

Page 29: Recursive implicit proofs with Shapeless HLists

Expanding HList proof to all product-like types with

shapeless.Generic

(Demo)

Page 30: Recursive implicit proofs with Shapeless HLists

Expanding HList proof to all product-like types

Person(name: String, age: Int)

Int :: HNil

A & B => CShow[A] & Show[B] => Show[A ::

B]Int

AShow[Int]

HNil

AShow[HNil]

String

AShow[String

]

String :: Int :: HNil

A & B => CShow[A] & Show[B] => Show[A ::

B]

Generic.Aux[Person, String :: Int :: HNil]

A (macro)Generic[T]

Person

A & B => CShow[R] & Generic.Aux[T,R] =>

Show[T]

Page 31: Recursive implicit proofs with Shapeless HLists

You can automatically derive typeclass instances for any case

class with zero boilerplate!

(given the property can be derived from the “shape” of the case class)

Expanding HList proof to all product-like types

Page 32: Recursive implicit proofs with Shapeless HLists

Usage examples:1. Typesafe serialization (e.g. circe JSON)2. Typesafe conversions between types3. Generic data diffs (e.g. for API testing)4. Tuple23, Tuple24, ... 5. ...

Expanding HList proof to all product-like types

Page 33: Recursive implicit proofs with Shapeless HLists

Aux Pattern

(Demo)

Page 34: Recursive implicit proofs with Shapeless HLists

Generic Product Diffs

(Demo)

Page 35: Recursive implicit proofs with Shapeless HLists

What’s next?

1. Type-level computations along the proof chain2. Implicit prioritisation – control potentially ambiguous implicits3. shapeless.Coproduct as a generic representation of sum types (sealed

traits)4. LabelledGeneric[A]: Generic, that knows about names of fields and

types.5. Full-blown JSON (de)serialization proof for arbitrary ADT hierarchies6. Generic transformations with Poly

Page 36: Recursive implicit proofs with Shapeless HLists

Thank you!