Slide 1

Slide 1 text

An encoding of Any for Leon Romain Ruetschi, LARA - EPFL Spring 2015 0

Slide 2

Slide 2 text

Motivation 1

Slide 3

Slide 3 text

Proving properties of unityped programs def reverse(lst: Any): Any = { if (lst == Nil()) Nil() else reverse(lst.tail) ++ Cons(lst.head, Nil()) } ensuring (_.contents == lst.contents) def reverseReverseIsIdentity(lst: Any) = { reverse(reverse(lst)) == lst }.holds 2

Slide 4

Slide 4 text

Implementation 3

Slide 5

Slide 5 text

Example case class Box(value: Int) def double(x: Any): Any = x match { case n: Int => n * 2 case Box(n) => Box(n * 2) case _ => x } double(42) 4

Slide 6

Slide 6 text

Example sealed abstract class Any1 case class Any1Int(value: Int) extends Any1 case class Any1Box(value: Box) extends Any1 def double(x: Any1): Any1 = x match { case Any1Int(n) => Any1Int(n * 2) case Any1Box(Box(n)) => Any1Box(Box(n * 2)) case _ => x } double(Any1Int(42)) 5

Slide 7

Slide 7 text

Outline ￿ Declare constructors ￿ Rewrite functions types to replace Any with Any1 ￿ Rewrite pattern matches to peel the constructors off ￿ Lift expressions into Any1 ￿ Extract implicit classes as case classes ￿ Add extension methods to Any 6

Slide 8

Slide 8 text

Declare constructors sealed abstract class Expr case class Lit(n: Int) extends Expr case class Add(l: Expr, r: Expr) extends Expr abstract class Any1 case class Any1Expr(value: Expr) extends Any1 case class Any1Int(value: Int) extends Any1 7

Slide 9

Slide 9 text

Rewrite functions types def id(x: Any): Any = x case class Box(value: Any) { def map(f: Any => Any): Box = Box(f(value)) def contains(other: Any): Boolean = value == other } 8

Slide 10

Slide 10 text

Rewrite functions types def id(x: Any1): Any1 = x case class Box(value: Any1) { def map(f: Any1 => Any1): Box = Box(f(value)) def contains(other: Any1): Boolean = value == other } 9

Slide 11

Slide 11 text

Lift expressions def id(x: Any1): Any1 = x def toAny(x: Int): Any1 = x def double(x: Any1): Any1 = x match { case i: Int => x * 2 case b: Boolean => !b case _ => x } 10

Slide 12

Slide 12 text

Lift expressions def id(x: Any1): Any1 = x def toAny(x: Int): Any1 = Any1Int(x) def double(x: Any1): Any1 = x match { case i: Int => Any1Int(i * 2) case b: Boolean => Any1Boolean(!b) case _ => x } 11

Slide 13

Slide 13 text

Rewrite pattern matches def double(x: Any1): Any1 = x match { case Any1Int(i: Int) => Any1Int(i * 2) case Any1Boolean(b: Boolean) => Any1Boolean(!b) case _ => x } 12

Slide 14

Slide 14 text

Extract implicit classes @library implicit class BooleanOps(val underlying: Boolean) { def holds : Boolean = { assert(underlying) underlying } def ==> (that: Boolean): Boolean = { !underlying || that } } 13

Slide 15

Slide 15 text

Extract implicit classes @library case class BooleanOps(underlying: Boolean) { def holds: Boolean = { assert(underlying) underlying } def ==> (that: Boolean): Boolean = { !underlying || that } } @library def BooleanOps(x: Boolean): BooleanOps = BooleanOps(x) 14

Slide 16

Slide 16 text

Add extension methods to Any @library implicit class Any1Ops(val lhs: Any) { def *(rhs: Any): Any = (lhs, rhs) match { case (l: Int, r: Int) => l * r case (l: BigInt, r: BigInt) => l * r case (l: String, r: BigInt) => l repeat r case _ => error[Any]("operation not supported") } } 15

Slide 17

Slide 17 text

Limitations 16

Slide 18

Slide 18 text

Generic types Because child types must form a simple bijection with parent class type, it is currently impossible to synthesize constructors for polymorphic type such as Option[T]: abstract class Any1 case class Any1Option[T](x: Option[T]) extends Any1 17

Slide 19

Slide 19 text

Structural types parametrized by Any Because ADTs cannot contain recursive references through non-structural types such as Set, it is currently impossible to define types such as: abstract class Any1 case class Any1SetAny(value: Set[Any1]) extends Any1 18

Slide 20

Slide 20 text

Hack 19

Slide 21

Slide 21 text

Mutable types id.setType(Any1Type) ￿ Current implementation mutates Identifiers type. ￿ Only meant as a temporary hack, will be fixed soon. 20

Slide 22

Slide 22 text

What’s left? 21

Slide 23

Slide 23 text

Preconditions Currently not possible to write: def reverse(lst: Any): Any = { require(lst.isInstanceOf[List]) if (lst == Nil()) Nil() else reverse(lst.tail) ++ Cons(lst.head, Nil()) } ensuring (_.size == lst.size) Can’t handle this in translation to Z3: require(lst.isInstanceOf[Any1List]) Without those preconditions, counter-examples are yielded for postconditions. 22

Slide 24

Slide 24 text

Match exhaustivity Implicit case classes show up as counter-examples, which is something we might not want. 23

Slide 25

Slide 25 text

Other applications 24

Slide 26

Slide 26 text

Other applications Type inference for uni-typed programs 25

Slide 27

Slide 27 text

Other applications Type inference for uni-typed programs Port theorems from ACL2 to Leon 25

Slide 28

Slide 28 text

Thank you 26