0

I would love to generate code from locale definitions directly, without interpretation. Example:

(* A locale, from the code point of view, similar to a class *)
locale MyTest =
  fixes L :: "string list"
  assumes distinctL: "distinct L"
  begin
    definition isInL :: "string => bool" where
      "isInL s = (s ∈ set L)"
  end

The assumptions to instantiate MyTest are executable and I can generate code for them

definition "can_instance_MyTest L = distinct L"  
lemma "can_instance_MyTest L = MyTest L"
  by(simp add: MyTest_def can_instance_MyTest_def)
export_code can_instance_MyTest in Scala file -

I can define a function to execute the isInL definition for arbitrary MyTest.

definition code_isInL :: "string list ⇒ string ⇒ bool option" where
"code_isInL L s = (if can_instance_MyTest L then Some (MyTest.isInL L s) else None)"

lemma "code_isInL L s = Some b ⟷ MyTest L ∧ MyTest.isInL L s = b"
  by(simp add: code_isInL_def MyTest_def can_instance_MyTest_def)

However, code export fails:

export_code code_isInL in Scala file -
No code equations for MyTest.isInL

Why do I want to do such a thing? I'm working with a locale in the context of a valid_graph similar to e.g. here but finite. Testing that a graph is valid is easy. Now I want to export the code of my graph algorithms into Scala. Of course, the code should run on arbitrary valid graphs.

I'm thinking of the Scala analogy similar to something like this:

class MyTest(L: List[String]) {
require(L.distinct)
def isInL(s: String): Bool = L contains s
}
chris
  • 4,988
  • 20
  • 36
corny
  • 7,824
  • 3
  • 14
  • 20

2 Answers2

2

One way to solve this is datatype refinement using invariants (see isabelle doc codegen section 3.3). Thereby the validity assumption (distinct L, in your case) can be moved into a new type. Consider the following example:

typedef 'a dlist = "{xs::'a list. distinct xs}"
morphisms undlist dlist
proof
  show "[] ∈ ?dlist" by auto
qed

This defines a new type whose elements are all lists with distinct elements. We have to explicitly set up this new type for the code generator.

lemma [code abstype]: "dlist (undlist d) = d"
  by (fact undlist_inverse)

Then, in the locale we have the assumption "for free" (since every element of the new type guarantees it; however, at some point we have to lift a basic set of operations from lists with distinct element to 'a dlists).

locale MyTest =
  fixes L :: "string dlist"
begin
  definition isInL :: "string => bool" where
    "isInL s = (s ∈ set (undlist L))"
end

At this point, we are able to give (unconditional) equations to the code generator.

lemma [code]: "MyTest.isInL L s ⟷ s ∈ set (undlist L)"
  by (fact MyTest.isInL_def)

export_code MyTest.isInL in Haskell file -
chris
  • 4,988
  • 20
  • 36
  • Using the version of Feb 12 2013 of codegen-doc, §3.3 is not very understandable (and starting with a typo in the first sentence ...). – corny Mar 19 '13 at 23:37
  • What does `morphisms undlist dlist` do? – corny Mar 19 '13 at 23:41
  • Inspecting the generated code, I can freely create a `dlist` which is not distinct and pass it to `isInL`. How can that be prevented? In my original example, the code tested the distinctness before executing `isInL`. – corny Mar 19 '13 at 23:55
  • @corny: With `typedef` a new type, say `n`, is introduced that is "carved out" from an existing type, say `t`. Then for every element `x` of type `n` there is a representative of type `t`, which is obtained by `Rep_n x`. The other direction, i.e., obtaining an abstract value of type `n` from a given element `x` of type `t`, is covered by the (underspecified) function `Abs_n` that is only defined on members of the carved-out set. Those are the default names. The `morphisms` annotation merely allows to change the names of these two functions. – chris Mar 20 '13 at 02:25
  • @corny: Concerning your comment about "Inspecting the generated code, I can freely create a `dlist` which is not distinct ...". Yes you can ;). But as long as the whole code is generated from Isabelle (and not modified by hand), you have the guarantee that this does never happen. Of course, if you want/have to mix generated code with manually written code that is a problem ... Maybe it would be possible to make the code generated turn such types into abstract types in the target language, I did not look into it yet. – chris Mar 20 '13 at 02:31
  • I still cannot accept this answer (unfortunately). I do not want to modify the generated code but use it together with hand-written code as some sort of library. A user could enter arbitrary lists at runtime. I need some code to verify that the `locale` `assumes` are fulfilled. This implies that some check must be done at runtime. In my answer (below), I try to solve this problem by generating code such that an exception is thrown when the `distinct L` is not fulfilled. Unfortunately, I cannot show total correctness of this code and can not accept my answer either. – corny Mar 20 '13 at 16:16
0

I found a method, thanks to chris' tips.

Define a function to test the prerequisites/assumptions to instantiate a MyTest

definition "can_instance_MyTest L = distinct L"  

The command term MyTest reveals that MyTest is of type string list => bool, this means that MyTest is a predicate that takes a parameter and tests if this parameter fulfills MyTest's assumptions. We introduce a code equation ([code]) that replaces MyTest with the executable instance tester. The code generator can now produce code for occurrences of e.g., MyTest [a,b,c]

lemma [code]: "MyTest = can_instance_MyTest"
  by(simp add:fun_eq_iff MyTest_def can_instance_MyTest_def)

export_code MyTest in Scala file -

We yield (I replaced List[Char] with String for readability):

def can_instance_MyTest[A : HOL.equal](l: List[A]): Boolean =
  Lista.distinct[A](l)

def myTest: (List[String]) => Boolean =
  (a: List[String]) => can_instance_MyTest[String](a)

More readable pseudo-code:

def myTest(l: List[String]): Boolean = l.isDistinct

Now we need executable code for isInL. We utilize the predefined constant undefined. This code throws an exception if L is not distinct.

definition code_isInL :: "string list ⇒ string ⇒ bool" where
"code_isInL L s = (if can_instance_MyTest L then s ∈ set L else undefined)"

export_code code_isInL in Scala file -

We yield:

def code_isInL(l: List[String], s:String): Boolean =
  (if (can_instance_MyTest[String](l)) Lista.member[String](l, s)
    else sys.error("undefined"))*)

We just need to show that the code_isInL is correct:

lemma "b ≠ undefined ⟹ code_isInL L s = b ⟷ MyTest L ∧ MyTest.isInL L s = b"
  by(simp add: code_isInL_def MyTest_def can_instance_MyTest_def MyTest.isInL_def)


(* Unfortunately, the other direction does not hold. The price of undefined. *)
lemma "¬ MyTest L  ⟹ code_isInL L s = undefined"
  by(simp add: code_isInL_def can_instance_MyTest_def MyTest_def)
corny
  • 7,824
  • 3
  • 14
  • 20