Meta all introduction in Isabelle - isabelle

I'm confused about the all introduction meta rule in Isabelle. The papers say it should be:
From P deduce ⋀ x. P whenever x is not a free variables in the asumptions.
This is confusing to me. I understand better wikipedia's one:
From (P y) deduce ⋀ x. P x whenever y is not free in the (implicit) assumptions and x is not free in P.
How is the meta-forall rule encoded in Isabelle? Here is the source code:
(*Forall introduction. The Free or Var x must not be free in the hypotheses.
[x]
:
A
------
⋀x. A
*)
fun forall_intr
(ct as Cterm {maxidx = maxidx1, t = x, T, sorts, ...})
(th as Thm (der, {maxidx = maxidx2, shyps, hyps, tpairs, prop, ...})) =
let
fun result a =
Thm (deriv_rule1 (Proofterm.forall_intr_proof x a) der,
{cert = join_certificate1 (ct, th),
tags = [],
maxidx = Int.max (maxidx1, maxidx2),
shyps = Sorts.union sorts shyps,
hyps = hyps,
tpairs = tpairs,
prop = Logic.all_const T $ Abs (a, T, abstract_over (x, prop))});
fun check_occs a x ts =
if exists (fn t => Logic.occs (x, t)) ts then
raise THM ("forall_intr: variable " ^ quote a ^ " free in assumptions", 0, [th])
else ();
in
(case x of
Free (a, _) => (check_occs a x hyps; check_occs a x (terms_of_tpairs tpairs); result a)
| Var ((a, _), _) => (check_occs a x (terms_of_tpairs tpairs); result a)
| _ => raise THM ("forall_intr: not a variable", 0, [th]))
end;
Suppose I am a mathematician with only some notions of programming. How would you convince me the piece of code below implements the meta-forall rule in a sensible manner?.

Related

Why can't I apply a single step of a function like I do with definition in Isabelle?

I'm trying to do:
datatype my_bool = true | false
value "true" (* it has value true with type my_bool *)
fun conj :: "my_bool ⇒ my_bool ⇒ my_bool" where
"conj true true = true" |
"conj _ _ = false"
lemma "conj true true = true"
apply (simp only: conj_def)
but I get error:
Undefined fact: "conj_def"⌂
I understand the error but not why I can't apply a single simp like I do with definitions. Is this possible with functions at all?
When you define a new constant using the command definition, a theorem like conj_def is provided automatically (actually, it is possible to control the name of this theorem). The command fun does not provide a theorem name_def automatically (where name is the name of the constant). However, it provides a variety of other theorems. You can see such theorems by typing print_theorems after the specification of a constant using the command fun. For example,
datatype my_bool = true | false
fun conj :: "my_bool ⇒ my_bool ⇒ my_bool"
where
"conj true true = true"
| "conj _ _ = false"
print_theorems
For example, in the code listing above the command fun provides the fact conj.simps, which is, most likely, what you were looking for:
lemma "conj true true = true"
by (simp only: conj.simps)
Technically, it is possible to recover the original definitional axioms in Isabelle/ML for any constant, including conj (some insight about the definitional principles can be gained from [1], but there could exist more specialized references for this):
theory Scratch
imports Main
keywords "get_da" :: diag
begin
ML‹
(*the implementation of axioms_of_ci and da_of_ci are based on elements of
the code HOL/Types_To_Sets/unoverloading.ML*)
local
fun match_args (Ts, Us) =
if Type.could_matches (Ts, Us)
then
Option.map Envir.subst_type
(
SOME (Type.raw_matches (Ts, Us) Vartab.empty)
handle Type.TYPE_MATCH => NONE
)
else NONE;
in
fun axioms_of_ci thy defs (c, T) =
let
val const_entry = Theory.const_dep thy (c, T);
val Uss = Defs.specifications_of defs (fst const_entry);
in
Uss
|> filter (fn spec => is_some (match_args (#lhs spec, snd const_entry)))
|> map (fn Us => (#def Us, #description Us))
end;
fun das_of_ci thy defs = axioms_of_ci thy defs
#> map #1
#> filter is_some
#> map (the #> try (Thm.axiom thy))
#> filter is_some
#> map (the #> Drule.abs_def);
end;
fun apdupr f x = (x, f x);
fun axioms_of_const ctxt (c, T) =
let
val thy = Proof_Context.theory_of ctxt
val defs = Theory.defs_of thy
in das_of_ci thy defs (c, T) end;
fun process_da t st =
let
val ctxt = Toplevel.context_of st
val const = t
|> Proof_Context.read_term_pattern ctxt
|> dest_Const
val _ = const
|> axioms_of_const ctxt
|> map (Thm.string_of_thm ctxt)
|> map writeln
in () end;
val tts_find_sbts = Outer_Syntax.command
\<^command_keyword>‹get_da›
"print definitional axioms"
(Parse.const >> (process_da #> Toplevel.keep));
›
datatype my_bool = true | false
fun conj :: "my_bool ⇒ my_bool ⇒ my_bool"
where
"conj true true = true"
| "conj _ _ = false"
print_theorems
lemma "conj true true = true"
by (simp only: conj.simps)
get_da conj_graph
get_da conj_sumC
get_da conj
text‹The type of the input to the command #{command get_da} is important:›
get_da ‹plus::nat⇒nat⇒nat›
get_da ‹plus::int⇒int⇒int›
end
However, as noted by Manuel Eberl in the comments, such axioms are not particularly useful for most practical purposes for the end users.
Isabelle version: Isabelle2020
References:
Haftmann F, Wenzel M. Local Theory Specifications in Isabelle/Isar. In: Berardi S, Damiani F, de’Liguoro U, editors. Types for Proofs and Programs. Heidelberg: Springer; 2009. p. 153–68.

Parse the string into a list of tuples

I am looking for a piece of code in F# that can parse this type of string:
"x=1,y=42,A=[1,3,4,8]"
into a list of tuples that looks like this:
[("x",1);("y",42);("A",1);("A",3);("A",4);("A",8)]
Thanks in advance :)
You can quite nicely solve this using the FParsec parser combinator library. This is manageable using regular expressions, but it's not very elegant. Parser combinators make it very clear what the grammar of the inputs that you can handle is. You can also easily add other features like whitespace.
The following actually produces a list of string * Value pairs where Value is a new data type, corresponding to the possible right-hand-sides in the input:
type Value = Int of int | List of int list
Now, you can do the parsing using the following:
let ident = identifier (IdentifierOptions())
let rhs =
// Right-hand-side is either an integer...
( pint32 |>> Int ) <|>
// Or a list [ .. ] of integers separated by ','
( pchar '[' >>. (sepBy pint32 (pchar ',')) .>> pchar ']' |>> List )
let tuple =
// A single tuple is an identifier = right-hand-side
ident .>> pchar '=' .>>. rhs
let p =
// The input is a comma separated list of tuples
sepBy tuple (pchar ',')
run p "x=1,y=42,A=[1,3,4,8]"
Sometimes a named regex makes for readable code, even if not the regex.
(?<id>\w+)=((\[((?<list>(\d+))*,?\s*)*\])|(?<number>\d+))
This reads: Identifier = [Number followed by comma or space, zero or more] | Number
let parse input =
[
let regex = Regex("(?<id>\w+)=((\[((?<list>(\d+))*,?\s*)*\])|(?<number>\d+))")
let matches = regex.Matches input
for (expr : Match) in matches do
let group name = expr.Groups.[string name]
let id = group "id"
let list = group "list"
let number = group "number"
if list.Success then
for (capture : Capture) in list.Captures do
yield (id.Value, int capture.Value)
else if number.Success then
yield (id.Value, int number.Value)
]
Test
let input = "var1=1, var2=2, list=[1, 2, 3, 4], single=[1], empty=[], bad=[,,], bad=var"
printfn "%A" (parse input)
Output
[("var1", 1); ("var2", 2); ("list", 1); ("list", 2); ("list", 3); ("list", 4); "single", 1)]
It's quite advisable to follow the approach outlined by Tomas Petricek's answer, employing the established FParsec parser combinator library.
For educational purposes, you might want to roll your own parser combinator, and for this endeavor Scott W.'s blog ("Understanding parser combinators", and "Building a useful set of parser combinators") contains valuable information.
The parsing looks quite similar:
// parse a list of integers enclosed in brackets and separated by ','
let plist = pchar '[' >>. sepBy1 pint (pchar ',') .>> pchar ']'
// parser for the right hand side, singleton integer or a list of integers
let intOrList = pint |>> (fun x -> [x]) <|> plist
// projection for generation of string * integer tuples
let ungroup p =
p |>> List.collect (fun (key, xs) -> xs |> List.map (fun x -> key, x))
// parser for an input of zero or more string value pairs separated by ','
let parser =
sepBy (letters .>> pchar '=' .>>. intOrList) (pchar ',')
|> ungroup
"x=1,y=42,A=[1,3,4,8]"
|> run parser
// val it : ((String * int) list * string) option =
// Some ([("x", 1); ("y", 42); ("A", 1); ("A", 3); ("A", 4); ("A", 8)], "")
This simple grammar still requires 15 or so parser combinators. Another difference is that for simplicity's sake the Parser type has been modeled on FSharp's Option type.
type Parser<'T,'U> = Parser of ('T -> ('U * 'T) option)
let run (Parser f1) x = // run the parser with input
f1 x
let returnP arg = // lift a value to a Parser
Parser (fun x -> Some(arg, x))
let (>>=) (Parser f1) f = // apply parser-producing function
Parser(f1 >> Option.bind (fun (a, b) -> run (f a) b))
let (|>>) p f = // apply function to value inside Parser
p >>= (f >> returnP)
let (.>>.) p1 p2 = // andThen combinator
p1 >>= fun r1 ->
p2 >>= fun r2 ->
returnP (r1, r2)
let (.>>) p1 p2 = // andThen, but keep first value only
(p1 .>>. p2) |>> fst
let (>>.) p1 p2 = // andThen, keep second value only
(p1 .>>. p2) |>> snd
let pchar c = // parse a single character
Parser (fun s ->
if String.length s > 0 && s.[0] = c then Some(c, s.[1..])
else None )
let (<|>) (Parser f1) (Parser f2) = // orElse combinator
Parser(fun arg ->
match f1 arg with None -> f2 arg | res -> res )
let choice parsers = // choose any of a list of combinators
List.reduce (<|>) parsers
let anyOf = // choose any of a list of characters
List.map pchar >> choice
let many (Parser f) = // matches zero or more occurrences
let rec aux input =
match f input with
| None -> [], input
| Some (x, rest1) ->
let xs, rest2 = aux rest1
x::xs, rest2
Parser (fun arg -> Some(aux arg))
let many1 p = // matches one or more occurrences of p
p >>= fun x ->
many p >>= fun xs ->
returnP (x::xs)
let stringP p = // converts list of characters to string
p |>> (fun xs -> System.String(List.toArray xs))
let letters = // matches one or more letters
many1 (anyOf ['A'..'Z'] <|> anyOf ['a'..'z']) |> stringP
let pint = // matches an integer
many1 (anyOf ['0'..'9']) |> stringP |>> int
let sepBy1 p sep = // matches p one or more times, separated by sep
p .>>. many (sep >>. p) |>> (fun (x,xs) -> x::xs)
let sepBy p sep = // matches p zero or more times, separated by sep
sepBy1 p sep <|> returnP []
Try this:
open System.Text.RegularExpressions
let input = "x=1,y=42,A=[1,3,4,8]"
Regex.Split(input,",(?=[A-Za-z])") //output: [|"x=1"; "y=42"; "A=[1,3,4,8]"|]
|> Array.collect (fun x ->
let l,v = Regex.Split(x,"=") |> fun t -> Array.head t,Array.last t //label and value
Regex.Split(v,",") |> Array.map (fun x -> l,Regex.Replace(x,"\[|\]","") |> int))
|> List.ofArray

Writing variable patterns in Isabelle/ML

I was studying ways to trasnlate:
apply(rewrite in "_ / ⌑" some_theorem)
into ML, and I came up with the following:
apply(tactic ‹
let
val pat = [
Rewrite.In,
Rewrite.Term (#{const divide(real)} $ Var (("c", 0), \<^typ>‹real›) $
Rewrite.mk_hole 1 (\<^typ>‹real›), []),
Rewrite.In
]
val to = NONE
in
CCONVERSION (Rewrite.rewrite_conv #{context} (pat, to) #{thms juanito_def}) 1
end
›)
here I am saying "please pattern match with any subterm of the term in the quotient". You may see previous edits of the question to see interesting patterns.
Some interesting conclusions of having used the library are
at : allows to select a subterm in which to pattern match
in : allows to specify all the subterms of a term
terms will be pattern-matched with the resulting patterns.
Not understood
Could you give an explanation of what the following tactic is doing?
lemma
assumes "Q (λb :: int. P (λa. a + b) (λa. a + b))"
shows "Q (λb :: int. P (λa. a + b) (λa. b + a))"
apply (tactic ‹
let
val (x, ctxt) = yield_singleton Variable.add_fixes "x" \<^context>
val pat = [
Rewrite.Concl,
Rewrite.In,
Rewrite.Term (Free ("Q", (\<^typ>‹int› --> TVar (("'b",0), [])) --> \<^typ>‹bool›)
$ Abs ("x", \<^typ>‹int›, Rewrite.mk_hole 1 (\<^typ>‹int› --> TVar (("'b",0), [])) $ Bound 0), [(x, \<^typ>‹int›)]),
Rewrite.In,
Rewrite.Term (#{const plus(int)} $ Free (x, \<^typ>‹int›) $ Var (("c", 0), \<^typ>‹int›), [])
]
val to = NONE
in CCONVERSION (Rewrite.rewrite_conv ctxt (pat, to) #{thms add.commute}) 1 end
›)
apply (fact assms)
done
The first Rewrite.Term and the fixing of a variable are a bit obscure to me.

ml function of type fn : 'a -> 'b

The function:
fn : 'a -> 'b
Now, are there any functions which can be defined and have this type?
There are two possible implementations for that function signature in Standard ML. One employs exceptions, the other recursion:
val raises : 'a -> 'b =
fn a => raise Fail "some error";
(* Infinite looping; satisfies the type signature, *)
(* but won't ever produce anything. *)
val rec loops : 'a -> 'b =
fn a => loops a;
The first solution may be useful for defining a helper function, say bug, which saves a few key strokes:
fun bug msg = raise Fail ("BUG: " ^ msg);
The other solution may be useful for defining server loops or REPLs.
In the Basis library, OS.Process.exit is such a function that returns an unknown generic type 'a:
- OS.Process.exit;
val it = fn : OS.Process.status -> 'a
A small echo REPL with type val repl = fn : unit -> 'a:
fun repl () =
let
val line = TextIO.inputLine TextIO.stdIn
in
case line of
NONE => OS.Process.exit OS.Process.failure
| SOME ":q\n" => OS.Process.exit OS.Process.success
| SOME line => (TextIO.print line ; repl ())
end
You might also find useful this question about the type signature of Haskell's forever function.
I can think of one example:
fun f a = raise Div;
I can think of several:
One that is recursive,
fun f x = f x
Any function that raises exceptions,
fun f x = raise SomeExn
Any function that is mutually recursive, e.g.,
fun f x = g x
and g x = f x
Any function that uses casting (requires specific compiler support, below is for Moscow ML),
fun f x = Obj.magic x
Breaking the type system like this is probably cheating, but unlike all the other functions with this type, this function actually returns something. (In the simplest case, it's the identity function.)
A function that throws if the Collatz conjecture is false, recurses infinitely if true,
fun f x =
let fun loop (i : IntInf.int) =
if collatz i
then loop (i+1)
else raise Collatz
in loop 1 end
which is really just a combination of the first two.
Any function that performs arbitrary I/O and recurses infinitely, e.g.
fun f x = (print "Woohoo!"; f x)
fun repl x =
let val y = read ()
val z = eval y
val _ = print z
in repl x end
One may argue that exceptions and infinite recursion represent the same theoretical value ⊥ (bottom) meaning "no result", although since you can catch exceptions and not infinitely recursive functions, you may also argue they're different.
If you restrict yourself to pure functions (e.g. no printing or exceptions) and only Standard ML (and not compiler-specific features) and you think of the mutually recursive cases as functionally equivalent in spite of their different recursion schemes, we're back to just fun f x = f x.
The reason why fun f x = f x has type 'a → 'b is perhaps obvious: The type-inference algorithm assumes that the input type and the output type are 'a and 'b respectively and goes on to conclude the function's only constraint: That f x's input type must be equal to f x's input type, and that f x's output type must be equal to f x's output type, at which point the types 'a and 'b have not been specialized any further.

Graph with sets as vertices

I have a tiny grammar represented as a variant type term with strings that are tokens/part of tokens (type term).
Given expressions from the grammar, I am collecting all strings from expressions and pack them into sets (function vars). Finally, I want to create some graph with these sets as vertices (lines 48-49).
For some reason, the graph created in the such sophisticated way does not recognise sets containing same variables and creates multiple vertices with the same content. I don't really understand why this is happening.
Here is minimal working example with this behaviour:
(* demo.ml *)
type term =
| Var of string
| List of term list * string option
| Tuple of term list
module SSet = Set.Make(
struct
let compare = String.compare
type t = string
end)
let rec vars = function
| Var v -> SSet.singleton v
| List (x, tail) ->
let tl = match tail with
| None -> SSet.empty
| Some var -> SSet.singleton var in
SSet.union tl (List.fold_left SSet.union SSet.empty (List.map vars x))
| Tuple x -> List.fold_left SSet.union SSet.empty (List.map vars x)
module Node = struct
type t = SSet.t
let compare = SSet.compare
let equal = SSet.equal
let hash = Hashtbl.hash
end
module G = Graph.Imperative.Digraph.ConcreteBidirectional(Node)
(* dot output for the graph for illustration purposes *)
module Dot = Graph.Graphviz.Dot(struct
include G
let edge_attributes _ = []
let default_edge_attributes _ = []
let get_subgraph _ = None
let vertex_attributes _ = []
let vertex_name v = Printf.sprintf "{%s}" (String.concat ", " (SSet.elements v))
let default_vertex_attributes _ = []
let graph_attributes _ = []
end)
let _ =
(* creation of two terms *)
let a, b = List ([Var "a"], Some "b"), Tuple [Var "a"; Var "b"] in
(* get strings from terms packed into sets *)
let avars, bvars = vars a, vars b in
let g = G.create () in
G.add_edge g avars bvars;
Printf.printf "The content is the same: [%s] [%s]\n"
(String.concat ", " (SSet.elements avars))
(String.concat ", " (SSet.elements bvars));
Printf.printf "compare/equal output: %d %b\n"
(SSet.compare avars bvars)
(SSet.equal avars bvars);
Printf.printf "Hash values are different: %d %d\n"
(Hashtbl.hash avars) (Hashtbl.hash bvars);
Dot.fprint_graph Format.str_formatter g;
Printf.printf "Graph representation:\n%s" (Format.flush_str_formatter ())
In order to compile, type ocamlc -c -I +ocamlgraph demo.ml; ocamlc -I +ocamlgraph graph.cma demo.cmo. When the program is executed you get this output:
The content is the same: [a, b] [a, b]
compare/equal output: 0 true
Hash values are different: 814436103 1017954833
Graph representation:
digraph G {
{a, b};
{a, b};
{a, b} -> {a, b};
{a, b} -> {a, b};
}
To sum up, I am curious why there are non-equal hash values for sets and two identical vertices are created in the graph, despite the fact these sets are equal by all other means.
I suspect the general answer is that OCaml's built-in hashing is based on rather physical properties of a value, while set equality is a more abstract notion. If you represent sets as ordered binary trees, there are many trees that represent the same set (as is well known). These will be equal as sets but might very well hash to different values.
If you want hashing to work for sets, you might have to supply your own function.
As Jeffrey pointed out, it seems that the problem is in the definition of the hash function that is part of Node module.
Changing it to let hash x = Hashtbl.hash (SSet.elements x) fixed the issue.

Resources