Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Translating Scala programs to Isabelle with Leon

A1216674d5c9747bcdcc716872439137?s=47 Lars Hupel
September 08, 2015

Translating Scala programs to Isabelle with Leon

Leon is a system for verifying functional Scala programs. It uses a variety of automated theorem provers (ATPs) to check verification conditions (VCs) stemming from the input program. This process is completely automatic: no user intervention is required for proving these conditions. Isabelle, on the other hand, is an interactive theorem prover used to verify mathematical specifications using its own input language Isabelle/Isar. Users specify inductive definitions and write proofs about them manually, albeit with the help of semi-automated tactics.

In this work, I have connected Isabelle as an underlying solver for Leon verification conditions. While Isabelle is much weaker in terms of automation, this has two advantages: Firstly, proofs in Isabelle, even those generated from automated tactics, have are justified against a minimal inference kernel. As opposed to ATPs, which are complex pieces of software, it is far less likely that a kernel-certified proof is unsound. Secondly, Isabelle's premier logic, HOL, has seen years of development of rich mathematical libraries. Proofs carried out in Isabelle have access to that, which means that there's a great potential for reuse of existing developments.

A1216674d5c9747bcdcc716872439137?s=128

Lars Hupel

September 08, 2015
Tweet

Transcript

  1. Transla ng Scala programs to Isabelle with Leon Lars Hupel

    Technische Universität München September 8, 2015
  2. Isabelle ▶ interactive proof assistant ▶ powerful automation ▶ classical

    and equational reasoning ▶ decision procedures (e.g. linear arithmetic) ▶ integration with external automated theorem provers ▶ ... ▶ based on a generic core logic 2 / 28
  3. Structure of an Isabelle theory System perspec ve ▶ foundational

    unit of knowledge in Isabelle is a theory ▶ a command transforms a theory: theory → theory ▶ theories can be merged: theory → theory → theory ▶ theories can be extended by (primitive) value and type definitions ▶ everything else needs to be constructed 3 / 28
  4. Structure of an Isabelle theory User perspec ve theory Words

    imports Bits Bool List begin typedef word = {x :: int | 0 ≤ x ∧ x < 232} definition word of int :: int → word where . . . lemma . . . end 4 / 28
  5. Extensibility ▶ Isabelle’s core logic doesn’t support much ▶ however,

    users can define their own commands and own syntax ▶ the most prominent logic (HOL) is defined in user-space ▶ including commands for functional programming 5 / 28
  6. Func onal Programming in Isabelle Datatypes datatype α list =

    Nil | Cons α (α list) This specification introduces: ▶ an induction principle ▶ a recursor rec list :: α → (β → β list → α → α) → β list → α ▶ ... a lot more 6 / 28
  7. Func onal Programming in Isabelle Datatypes datatype α list =

    Nil | Cons α (α list) This specification is ... ▶ checked for wellformedness and inhabitants ▶ datatype α tree = Node (α tree set) is not admitted ▶ not axiomatic, but constructed! ▶ solution of the equation: τ = unit + (α × τ) 6 / 28
  8. Func onal Programming in Isabelle Func ons ▶ definition principle

    of the logic: x def = λy1 . . . yn . t ▶ ... introduces a new constant and an axiom ▶ but: t must not depend on x itself ▶ primrec defines a function over a datatype using its recursor ▶ fun allows more flexible recursion, but need to prove termination 7 / 28
  9. Example Defini ons datatype α list = Nil | Cons

    α (α list) primrec length :: α list → int where length Nil = 0 length (Cons x xs) = 1 + length xs 8 / 28
  10. Example Defini ons datatype α list = Nil | Cons

    α (α list) primrec length :: α list → int where length Nil = 0 length (Cons x xs) = 1 + length xs Proofs lemma length xs ≥ 0 by (induct xs) simp+ 8 / 28
  11. Func onal Programming in Scala more specifically: Leon’s subset of

    Scala ▶ OOP-style datatypes ▶ methods ▶ pattern matching with guards and type casing ▶ generics 9 / 28
  12. Example sealed abstract class List[A] case class Cons[A](head: A, tail:

    List[A]) extends List[A] case class Nil[A]() extends List[A] def length(xs: List[A]): BigInt = xs match { case Nil() => 0 case Cons(_, xs) => 1 + length(xs) } ensuring { _ >= 0 } 10 / 28
  13. Isabelle as a solver in Leon Isabelle has two modes

    of operation: interac ve user types code into the IDE, checked continuously batch command line process checks a list of source files once Batch mode is comparable to most compilers 11 / 28
  14. Isabelle as a solver in Leon Problems ▶ starting up

    an Isabelle process takes a couple of seconds ▶ Leon creates a solver instance for each verification condition ▶ an Isabelle batch process cannot be reused 12 / 28
  15. Isabelle as a solver in Leon Problems ▶ starting up

    an Isabelle process takes a couple of seconds ▶ Leon creates a solver instance for each verification condition ▶ an Isabelle batch process cannot be reused Solu on ▶ use the interactive mode ▶ create one instance when Leon starts, reuse it 12 / 28
  16. Bridging the gap Mantra: as little unverified code as possible

    13 / 28
  17. Bridging the gap Problem ▶ Input: Leon (≈ Scala) AST

    ▶ Translation: ??? ▶ Output: Isabelle ??? 14 / 28
  18. Bridging the gap Problem ▶ Input: Leon (≈ Scala) AST

    ▶ Translation: ??? ▶ Output: Isabelle ??? Possible solu on Textual code generation! 14 / 28
  19. Bridging the gap Problem ▶ Input: Leon (≈ Scala) AST

    ▶ Translation: ??? ▶ Output: Isabelle ??? Be er solu on Talk directly to Isabelle’s API 14 / 28
  20. Bridging the gap Problem ▶ Input: Leon (≈ Scala) AST

    ▶ Translation: ??? ▶ Output: Isabelle ??? Be er solu on Talk directly to Isabelle’s API ... except I have to implement the API myself 14 / 28
  21. Talking to Isabelle ▶ there is a Scala API for

    Isabelle, but it’s very low-level ▶ I’ve implemented a high-level wrapper: libisabelle ▶ supports multiple Isabelle versions ▶ supports multiple simultaneous processes ▶ can be used like an asynchronous RPC library ▶ downloads and installs Isabelle automatically 15 / 28
  22. Isabelle API 16 / 28

  23. Transla on from Scala to Isabelle Datatypes Datatypes are almost

    straightforward. 17 / 28
  24. Transla on from Scala to Isabelle Func ons Functions are

    quite complicated. 18 / 28
  25. Transla ng func ons Scala input def length(xs: List[A]): BigInt

    = xs match { case Nil() => 0 case Cons(_, xs) => 1 + length(xs) } 19 / 28
  26. Transla ng func ons Scala input def length(xs: List[A]): BigInt

    = xs match { case Nil() => 0 case Cons(_, xs) => 1 + length(xs) } Naive Isabelle output definition length xs = (case xs of Nil ⇒ 0 | Cons x xs ⇒ 1 + length xs) 19 / 28
  27. Transla ng func ons Scala input def length(xs: List[A]): BigInt

    = xs match { case Nil() => 0 case Cons(_, xs) => 1 + length(xs) } Ambi ous Isabelle output fun length xs = (case xs of Nil ⇒ 0 | Cons x xs ⇒ 1 + length xs) 19 / 28
  28. Transla ng func ons Scala input def length(xs: List[A]): BigInt

    = xs match { case Nil() => 0 case Cons(_, xs) => 1 + length(xs) } Desired Isabelle output fun length where length Nil = 0 length (Cons x xs) = 1 + length xs 19 / 28
  29. Recursive func ons ▶ detecting whether a function is recursive

    or not: trivial ▶ pick either fun or defini on ▶ but: mutually recursive functions must be defined together ▶ ... without any additional functions 20 / 28
  30. Transla ng func ons Algorithm for recursive func ons 1.

    compute strongly connected components of call graph 2. define functions of each components together, using case expressions 3. postprocess into implicit pattern matching 4. obtain better induction principle This procedure takes ≈ 40 seconds on a fast machine for the Leon library. 21 / 28
  31. Transla ng type informa on ▶ “best effort” approach ▶

    in Isabelle, each atomic term stores its fully instantiated type ▶ except bound variables: types stored in enclosing term ▶ Example: f x1 x2 . . . ▶ f contains the types of the xi ▶ leave out redundant information, rely on type inference for the rest 22 / 28
  32. Further issues Binders Issue Isabelle’s term language uses de Bruijn

    indices Solu on ▶ keep track of identifiers when recursing through binders ▶ caveat: open terms cannot be type checked 23 / 28
  33. Further issues Pa ern matching Issue Isabelle’s pattern matching is

    just syntactic sugar Example Syntax case xs of Nil ⇒ 0 | Cons x xs ⇒ 1 + length xs Term case list 0 (λx xs. 1 + length xs) xs Solu on ▶ use Isabelle’s built-in translations ▶ but: clauses need to be type checked ▶ when pattern matching under binders, invent dummy variables 24 / 28
  34. Further issues Pa ern matching Issue Isabelle requires unambiguous patterns

    Example case xs of Cons x xs ⇒ . . . | xs ⇒ . . . 25 / 28
  35. Further issues Pa ern matching Issue Isabelle requires unambiguous patterns

    Example case xs of Cons x xs ⇒ . . . | xs ⇒ . . . “I have some experience with the combinatorial explosion [...] You don’t need much: just take extended regular expressions (≈ 10 constructors) and define binary normalizing constructors (by some sequential pattern matching on both arguments). [...] is full of ≈ 30 sec [function] declarations.” 25 / 28
  36. Further issues Unsupported constructs Issue What to do with unsupported

    Leon constructs? Solu on ▶ in Isabelle’s logic, every type is inhabited ▶ unspecified constant undefined :: α ▶ but: reflexivity holds ▶ trick: use undefined with an additional unique number 26 / 28
  37. Trustworthiness ▶ Scala and Isabelle/HOL are quite different ▶ translation

    is more intricate than I’d like it to be ▶ user doesn’t see resulting terms and definitions ▶ countermeasure: print report ▶ looks almost like an Isabelle theory ▶ can be copy-pasted into IDE ▶ option: dump full-blown files 27 / 28
  38. Q & A