theory Demo11 imports Main "HOL-Library.Product_Lexorder" (* provides linear order on pairs *) "HOL-Library.Code_Target_Numeral" (* implement int and nat via target-language integers *) Demo09 (* ordered trees *) begin section \<open>Code Generation using Target Language Types\<close> definition mod_on_int :: "integer \<Rightarrow> integer \<Rightarrow> integer" where "mod_on_int x y = x mod y" text \<open>In order to guarantee safe implementation, a frontend is used that handles all the potentially corner cases, such that eventually a mod-function of the target language is invoked where both arguments are positive integers.\<close> (* there is a slight deviation from the slides, since actually a divmod-operation is mapped to a target-language divmod operation *) thm divmod_integer_code[of x y] (* this corresponds to code-equation in slides *) thm Code_Numeral.divmod_abs_def[of x y] (* this corresponds to target_mod in slides *) export_code mod_on_int in Haskell module_name Mod text \<open>Natural numbers are wrapped into their own datatype.\<close> definition test_on_nat :: "nat \<Rightarrow> nat" where "test_on_nat n = (n - 5) + n" export_code test_on_nat in Haskell module_name Nat section \<open>Code Generation with Subtypes\<close> typedef large_int = "{ n :: integer . n > 1000}" by (intro exI[of _ 1001], auto) setup_lifting type_definition_large_int lift_definition get_int :: "large_int \<Rightarrow> integer" is "\<lambda> x. x" . lift_definition add_10 :: "large_int \<Rightarrow> large_int" is "\<lambda> x. x + 10" by simp lemma large: "1000 < get_int x" by transfer auto lemma "1000 < (5 :: integer)" proof - have "1000 < get_int (Abs_large_int 5)" by (rule large) also have "\<dots> = Rep_large_int (Abs_large_int 5)" by (rule get_int.rep_eq) also have "\<dots> = 5" by eval oops (* problem: code equation is usually conditional *) thm Abs_large_int_inverse (* but in generated code the condition is just erased *) code_thms get_int lemma "Rep_large_int (Abs_large_int 5) = 5" by code_simp (* code generator checks that abstraction functions are only applied safely, e.g., via lift_definition where a proof is required at definition time *) (* hiding Abs_large_int in a definition is also detected *) definition foo :: "integer \<Rightarrow> large_int" where "foo x = Abs_large_int x" definition bar :: "integer \<Rightarrow> large_int" where "bar x = Abs_large_int (x * x + 5000)" value (code) "foo 5" value (code) "bar 5" lift_definition bar' :: "integer \<Rightarrow> large_int" is "\<lambda> x. x * x + 5000" by (simp add: add_sign_intros(1)) value (code) "bar' 5" export_code get_int add_10 bar' in Haskell module_name Subtype_Code (* in the generated code, there is no such safety mechanism, i.e., here it is the users responsibility to not build expressions like "Abs_large_int 5" *) section \<open>Data Refinement\<close> text \<open>*** copy of Isabelle code of previous Demo10 ***\<close> definition reach :: "'a rel \<Rightarrow> 'a set \<Rightarrow> 'a set" where "reach G A = {y. \<exists>x\<in>A. (x, y) \<in> G^*}" lemma [code]: "reach G A = (if A = {} then {} else let A_edges = Set.filter (\<lambda> (x,y). x \<in> A) G; successors = snd ` A_edges in A \<union> reach (G - A_edges) successors)" unfolding reach_def sorry text \<open>*** end of copy ***\<close> value (code) "reach {(1,2 :: nat), (3,4), (2,4), (4,1)} {1}" export_code reach in Haskell module_name Reach_List subsection \<open>Implementation of Set-Operations via Ordered Trees\<close> (* check on empty set *) fun is_empty_t :: "'a tree \<Rightarrow> bool" where "is_empty_t Leaf = True" | "is_empty_t _ = False" lemma is_empty_t[simp]: "is_empty_t t = Set.is_empty (set_t t)" by (cases t, auto simp: Set.is_empty_def) (* for image, we convert to list and then do repeated insertion *) fun to_list_t :: "'a tree \<Rightarrow> 'a list" where "to_list_t Leaf = []" | "to_list_t (Node l x r) = x # to_list_t l @ to_list_t r" lemma to_list_t[simp]: "set (to_list_t t) = set_t t" by (induct t, auto) fun from_list_t :: "'a :: linorder list \<Rightarrow> 'a tree" where "from_list_t [] = Leaf" | "from_list_t (x # xs) = insert_t x (from_list_t xs)" lemma set_from_list_t[simp]: "set_t (from_list_t xs) = set xs" by (induct xs, auto) lemma ordered_from_list_t[simp]: "ordered (from_list_t xs)" by (induct xs, auto simp: ordered_insert_t ordered.intros) definition image_t :: "('a \<Rightarrow> 'b :: linorder) \<Rightarrow> 'a tree \<Rightarrow> 'b tree" where "image_t f t = from_list_t (map f (to_list_t t))" lemma image_t[simp]: "ordered (image_t f t)" "set_t (image_t f t) = f ` set_t t" unfolding image_t_def by auto (* for union and filter we are lazy and do not implement them now, but instead use the next layer; note that our implementation is not tuned on efficiency *) subsection \<open>Implementation of Set-Operations on Ordered Trees\<close> lift_definition to_list_o :: "'a :: linorder otree \<Rightarrow> 'a list" is to_list_t . lift_definition from_list_o :: "'a :: linorder list \<Rightarrow> 'a otree" is from_list_t by auto lift_definition image_o :: "('a :: linorder \<Rightarrow> 'b :: linorder) \<Rightarrow> 'a otree \<Rightarrow> 'b otree" is image_t by auto lift_definition is_empty_o :: "'a :: linorder otree \<Rightarrow> bool" is is_empty_t . definition "union_o t1 t2 = from_list_o (to_list_o t1 @ to_list_o t2)" definition "filter_o f t = from_list_o (filter f (to_list_o t))" lemma is_empty_o[simp]: "is_empty_o t = Set.is_empty (set_o t)" by transfer auto lemma to_list_o[simp]: "set (to_list_o t) = set_o t" by transfer auto lemma from_list_o[simp]: "set_o (from_list_o xs) = set xs" by transfer auto lemma image_o[simp]: "set_o (image_o f t) = f ` set_o t" by transfer auto lemma union_o[simp]: "set_o (union_o t1 t2) = set_o t1 \<union> set_o t2" unfolding union_o_def by auto lemma filter_o[simp]: "set_o (filter_o f t) = Set.filter f (set_o t)" unfolding filter_o_def by auto lemmas soundness_o = empty_o image_o member_o is_empty_o union_o filter_o insert_o subsection \<open>Use Ordered Trees to Implement Set-Type\<close> (* delete existing implementation *) declare [[code drop: Set.insert Set.member Set.union Set.filter Set.empty Set.image Set.is_empty Set.minus_set_inst.minus_set]] (* declare that set_o :: "'a tree \<Rightarrow> 'a set" is viewed as constructor for sets, i.e., datatype 'a set = set_o "'a tree" *) code_datatype set_o thm soundness_o thm soundness_o[symmetric] declare soundness_o[symmetric, code] (* only minus is missing: we just invoke filter which is fine *) lemma minus_impl[code]: "X - Y = Set.filter (\<lambda> x. x \<notin> Y) X" unfolding Set.filter_def by auto (* now that all required set operations are implemented we can evaluate reach *) value (code) "reach {(1,2 :: nat), (3,4), (2,4), (4,1)} {1}" (* result shows internal structure of tree! *) code_thms reach export_code reach in Haskell module_name Reach_Tree (* side-effect of using ordered trees: sets are only available for types that are linearly ordered *) datatype Test = A | B | C | D value (code) "reach {(A,B), (C,D), (B,D), (D,A)} {A}" end