more work on the storing section
authorChristian Urban <urbanc@in.tum.de>
Fri, 02 Oct 2009 15:38:14 +0200
changeset 327 ce754ad78bc9
parent 326 f76135c6c527
child 328 c0cae24b9d46
more work on the storing section
ProgTutorial/FirstSteps.thy
ProgTutorial/Parsing.thy
ProgTutorial/ROOT.ML
ProgTutorial/Recipes/Config.thy
ProgTutorial/Recipes/StoringData.thy
progtutorial.pdf
--- a/ProgTutorial/FirstSteps.thy	Thu Oct 01 10:19:21 2009 +0200
+++ b/ProgTutorial/FirstSteps.thy	Fri Oct 02 15:38:14 2009 +0200
@@ -187,8 +187,8 @@
   be displayed by the infrastructure.
 
 
-  (FIXME Mention how to work with @{ML_ind  debug in Toplevel} and 
-  @{ML_ind  profiling in Toplevel}.)
+  \footnote{\bf FIXME Mention how to work with @{ML_ind  debug in Toplevel} and 
+  @{ML_ind  profiling in Toplevel}.}
 *}
 
 (* FIXME
@@ -610,7 +610,7 @@
   "acc_incs 1 ||>> (fn x => (x, x + 2))"
   "(((((\"\", 1), 2), 3), 4), 6)"}
 
-  (FIXME: maybe give a ``real world'' example for this combinator)
+  \footnote{\bf FIXME: maybe give a ``real world'' example for this combinator.}
 *}
 
 text {*
@@ -678,9 +678,9 @@
   contains further information about combinators.
   \end{readmore}
 
-  (FIXME: find a good exercise for combinators)
+  \footnote{\bf FIXME: find a good exercise for combinators}
 
-  (FIXME: say something about calling conventions) 
+  \footnote{\bf FIXME: say something about calling conventions} 
 *}
 
 
@@ -798,9 +798,9 @@
   @{text "> "}~@{thm TrueConj_def}
   \end{isabelle}
 
-  (FIXME give a better example why bindings are important; maybe
+  \footnote{\bf FIXME give a better example why bindings are important; maybe
   give a pointer to \isacommand{local\_setup}; if not, then explain
-  why @{ML snd} is needed)
+  why @{ML snd} is needed.}
 
   It is also possible to define your own antiquotations. But you should
   exercise care when introducing new ones, as they can also make your code
@@ -844,20 +844,32 @@
   \end{readmore}
 *}
 
-section {* Storing Data in Isabelle (TBD) *}
+section {* Storing Data in Isabelle *}
 
 text {*
-  Isabelle provides mechanisms to store (and retrieve) arbitrary data. Before we 
-  explain them, let us digress: Convenitional wisdom has it that 
-  ML's type-system ensures that for example an @{ML_type "'a list"} can only
-  hold elements of the same type, namely @{ML_type "'a"}. Still, by some arguably
-  accidental features of ML, one can implement a universal type. In Isabelle it
-  is implemented as @{ML_type Universal.universal}. This type allows one to
-  inject and project elements of different type into for example into a list.
-  We will show this by storing an integer and boolean in a list. What is important
-  that access to the data is done in a type-safe manner. 
+  Isabelle provides mechanisms for storing (and retrieving) arbitrary
+  data. Before we delve into the details, let us digress a bit. Conventional
+  wisdom has it that the type-system of ML ensures that for example an
+  @{ML_type "'a list"} can only hold elements of the same type, namely
+  @{ML_type "'a"}. Despite this wisdom, however, it is possible to implement a
+  universal type in ML, although by some arguably accidental features of ML.
+  This universal type can be used to store data of different type into a single list.
+  It allows one to inject and to project data of \emph{arbitrary} type. This is
+  in contrast to datatypes, which only allow injection and projection of data for
+  some fixed collection of types. In light of the conventional wisdom cited
+  above it is important to keep in mind that the universal type does not
+  destroy type-safety of ML: storing and accessing the data can only be done
+  in a type-safe manner.
 
-  Let us first define projection and injection functions for booleans and integers.
+  \begin{readmore}
+  In Isabelle the universal type is implemented as the type @{ML_type
+  Universal.universal} in the file
+  @{ML_file "Pure/ML-Systems/universal.ML"}.
+  \end{readmore}
+
+  We will show the usage of the universal type by storing an integer and
+  a boolean into a single list. Let us first define injection and projection 
+  functions for booleans and integers into and from @{ML_type Universal.universal}.
 *}
 
 ML{*local
@@ -871,34 +883,216 @@
 end*}
 
 text {*
-  We can now build a list injecting @{ML_text "13"} and @{ML_text "true"} as
-  its two elements.
-*}
-
-ML{* val list = [inject_int 13, inject_bool true]*}
-
-ML {*
- project_int (nth list 0)
+  Using the injection functions, we can inject the integer @{ML_text "13"} 
+  and the boolean @{ML_text "true"} into @{ML_type Universal.universal}, and
+  then store them in a @{ML_type "Universal.universal list"} as follows:
 *}
 
-ML {*
- project_bool (nth list 1)
+ML{*val foo_list = 
+let
+  val thirteen  = inject_int 13
+  val truth_val = inject_bool true
+in
+  [thirteen, truth_val]
+end*}
+
+text {*
+  The data can be retrieved using the projection functions.
+  
+  @{ML_response [display, gray]
+"(project_int (nth foo_list 0), project_bool (nth foo_list 1))" 
+"(13, true)"}
+
+  Notice that we access the integer as an integer and the boolean as
+  a boolean. If we attempt to access the integer as a boolean, then we get 
+  a runtime error. 
+  
+  @{ML_response_fake [display, gray]
+"project_bool (nth foo_list 0)"  
+"*** Exception- Match raised"}
+
+  This runtime error is the reason why ML is still type-sound despite
+  containing a universal type.
+
+  Now, Isabelle heavily uses this mechanism to store all sorts of data: theorem lists,
+  simpsets, facts etc.  Roughly speaking, there are two places where data can 
+  be stored: in \emph{theories} and in \emph{proof contexts}. Again
+  roughly speaking, data such as simpsets need to be stored in a theory,
+  since they need to be maintained across proofs and even theories.
+  On the other hands, data such as facts change inside a proof and
+  therefore need to be maintained inside a proof 
+  context.\footnote{\bf TODO: explain more bout this.} 
+
+  For both theories and proof contexts there are convenient functors that help
+  with the data storage. Below we show how to implement a table in which we
+  can store theorems that can be looked up according to a string key. The
+  intention is to be able to look up introduction rules for logical
+  connectives. Such a table might be useful in an automatic proof procedure
+  and therefore it makes sense to store this data inside a theory.  The code
+  for the table is:
 *}
 
-ML {* 
-  Context.the_thread_data ();
-  Context.display_names @{theory};
-  Context.pretty_thy @{theory}
-    |> Pretty.string_of
-    |> tracing
+ML %linenosgray{*structure Data = TheoryDataFun
+  (type T = thm Symtab.table
+   val empty = Symtab.empty
+   val copy = I
+   val extend = I
+   fun merge _ = Symtab.merge (K true))*}
+
+text {*
+  In order to store data in a theory, we have to specify the type of the data
+  (Line 2). In this case we specify the type @{ML_type "thm Symtab.table"}, which 
+  stands for tables in which @{ML_type string}s can be looked up producing an associated
+  @{ML_type thm}. We also have to specify four functions: how to initialise
+  the data storage (Line 3), how to copy it (Line 4), how to extend it (Line
+  5) and how two tables should be merged (Line 6). These functions correspond
+  roughly to the operations performed on theories.\footnote{\bf FIXME: Say more
+  about the assumptions of these operations.} The result structure @{ML_text Data}
+  contains functions for accessing the table (@{ML Data.get}) and for updating
+  it (@{ML Data.map}). There are also two more functions, which however we
+  ignore here. Below we define two auxiliary functions, which help us with
+  accessing the table.
+*}
+
+ML{*val lookup = Symtab.lookup o Data.get
+fun update k v = Data.map (Symtab.update (k, v))*}
+
+text {*
+  Since we want to store introduction rules associated with their 
+  logical connective, we can fill the table as follows.
+*}
+
+setup %gray {*
+  update "op &"   @{thm conjI} #>
+  update "op -->" @{thm impI}  #>
+  update "All"    @{thm allI}
 *}
 
 text {*
+  The use of \isacommand{setup} makes sure the table in the current theory 
+  is updated. The lookup can now be performed as follows.
+
+  @{ML_response_fake [display, gray]
+"lookup @{theory} \"op &\""
+"SOME \"\<lbrakk>?P; ?Q\<rbrakk> \<Longrightarrow> ?P \<and> ?Q\""}
+
+  An important point to note is that these tables (and data in general)
+  need to be treated in a purely functional fashion. Although
+  we can update the table as follows
+*}
+
+setup %gray {* update "op &" @{thm TrueI} *}
+
+text {*
+  and lookup now produces the introduction rule for @{term "True"}
+  
+@{ML_response_fake [display, gray]
+"lookup @{theory} \"op &\""
+"SOME \"True\""}
+
+  there are no references involved. This is one of the most fundamental
+  coding conventions for programming in Isabelle. References would interfere
+  with the multithreaded execution model of Isabelle.\footnote{\bf FIXME: say more}
+  
   \begin{readmore}
-  @{ML_file "Pure/ML-Systems/universal.ML"}
+  Isabelle contains implementations of several container data structures,
+  including association lists in @{ML_file "Pure/General/alist.ML"},
+  tables and symtables in @{ML_file "Pure/General/table.ML"}, and
+  directed graphs in @{ML_file "Pure/General/graph.ML"}.
+  \end{readmore}
+
+  Storing data in a proof context is similar. With the following code we
+  can store a list of terms in a proof context. 
+*}
+
+ML{*structure Data = ProofDataFun
+  (type T = term list
+   val init = (K []))*}
+
+text {*
+  The function we have to specify has to produce a list once a context 
+  is initialised (taking the theory into account from which the 
+  context is derived). We choose to just return the empty list. Next 
+  we define two auxiliary functions for updating the list with a given
+  term and printing the list. 
+*}
+
+ML{*fun update trm = Data.map (fn xs => trm::xs)
+
+fun print ctxt =
+  case (Data.get ctxt) of
+    [] => tracing "Empty!"
+  | xs => tracing (string_of_terms ctxt xs)*}
+
+text {*
+  Next we start with the context given by @{text "@{context}"} and 
+  update it in various ways. 
+
+  @{ML_response_fake [display,gray]
+"let
+  val ctxt    = @{context}
+  val ctxt'   = ctxt   |> update @{term \"False\"}
+                       |> update @{term \"True \<and> True\"} 
+  val ctxt''  = ctxt   |> update @{term \"1::nat\"}
+  val ctxt''' = ctxt'' |> update @{term \"2::nat\"}
+in
+  print ctxt; 
+  print ctxt';
+  print ctxt'';
+  print ctxt'''
+end"
+"Empty!
+True \<and> True, False
+1
+2, 1"}
+
+  Many functions in Isabelle manage and update data in a similar
+  fashion. Consequently, such calculation with contexts occur frequently in
+  Isabelle code, although the ``context flow'' is usually only linear.
+
+  A special instance of the mechanisms described above are configuration
+  values. They are used to in order to configure tools by the user without
+  having to resort to the ML-level. Assume you want the user to control three
+  values, say @{text bval} containing a boolean, @{text ival} containing an
+  integer and @{text sval} containing a string. These values can be declared
+  by
+*}
+
+ML{*val (bval, setup_bval) = Attrib.config_bool "bval" false
+val (ival, setup_ival) = Attrib.config_int "ival" 0
+val (sval, setup_sval) = Attrib.config_string "sval" "some string" *}
+
+text {* 
+  where each value needs to be given a default. To enable these values, they need to 
+  be set up with
+*}
+
+setup %gray {* 
+  setup_bval #> 
+  setup_ival 
+*}
+
+text {*
+  The user can now manipulate the values from the user-level of Isabelle 
+  with the command
+*}
+
+declare [[bval = true, ival = 3]]
+
+text {*
+  On the ML-level these values can be retrieved using the 
+  function @{ML Config.get}:
+
+  @{ML_response [display,gray] "Config.get @{context} bval" "true"}
+
+  \begin{readmore}
+  For more information about configuration values see 
+  @{ML_file "Pure/Isar/attrib.ML"} and @{ML_file "Pure/config.ML"}.
   \end{readmore}
 *}
 
+
+
 (**************************************************)
 (* bak                                            *)
 (**************************************************)
--- a/ProgTutorial/Parsing.thy	Thu Oct 01 10:19:21 2009 +0200
+++ b/ProgTutorial/Parsing.thy	Fri Oct 02 15:38:14 2009 +0200
@@ -697,6 +697,7 @@
 | evenS: "odd n \<Longrightarrow> even (Suc n)"
 | oddS: "even n \<Longrightarrow> odd (Suc n)"
 
+
 text {*
   For this we are going to use the parser:
 *}
@@ -980,10 +981,10 @@
 
 text {* 
   but you will not see any action as we chose to implement this command to do
-  nothing. The point of this command is to only show the procedure of how
+  nothing. The point of this command is only to show the procedure of how
   to interact with ProofGeneral. A similar procedure has to be done with any 
   other new command, and also any new keyword that is introduced with 
-  @{ML_ind OuterKeyword.keyword}.
+  the function @{ML_ind keyword in OuterKeyword}. For example:
 *}
 
 ML{*val _ = OuterKeyword.keyword "blink" *}
@@ -1005,13 +1006,12 @@
 
 ML{*let
   fun trace_prop str = 
-     LocalTheory.theory (fn lthy => (tracing str; lthy))
+     LocalTheory.theory (fn ctxt => (tracing str; ctxt))
 
-  val trace_prop_parser = OuterParse.prop >> trace_prop
   val kind = OuterKeyword.thy_decl
 in
   OuterSyntax.local_theory "foobar_trace" "traces a proposition" 
-    kind trace_prop_parser
+    kind (OuterParse.prop >> trace_prop)
 end *}
 
 text {*
@@ -1036,28 +1036,28 @@
   OuterKeyword} to @{ML thy_goal in OuterKeyword} then the keyword file needs
   to be re-created!
 
-  Below we change \isacommand{foobar\_trace} so that it takes a proposition as
-  argument and then starts a proof in order to prove it. Therefore in Line 13, 
-  we set the kind indicator to @{ML thy_goal in OuterKeyword}.
+  Below we show the command \isacommand{foobar\_goal} which takes a
+  proposition as argument and then starts a proof in order to prove
+  it. Therefore in Line 9, we set the kind indicator to @{ML thy_goal in
+  OuterKeyword}.
 *}
 
 ML%linenosgray{*let
-  fun prove_prop str lthy =
+  fun goal_prop str lthy =
     let
       val prop = Syntax.read_prop lthy str
     in
       Proof.theorem_i NONE (K I) [[(prop,[])]] lthy
-    end;
+    end
   
-  val prove_prop_parser = OuterParse.prop >> prove_prop 
   val kind = OuterKeyword.thy_goal
 in
-  OuterSyntax.local_theory_to_proof "foobar_goal" "proving a proposition" 
-    kind prove_prop_parser
+  OuterSyntax.local_theory_to_proof "foobar_goal" "proves a proposition" 
+    kind (OuterParse.prop >> goal_prop)
 end *}
 
 text {*
-  The function @{text prove_prop} in Lines 2 to 7 takes a string (the proposition to be
+  The function @{text goal_prop} in Lines 2 to 7 takes a string (the proposition to be
   proved) and a context as argument.  The context is necessary in order to be able to use
   @{ML_ind  read_prop in Syntax}, which converts a string into a proper proposition.
   In Line 6 the function @{ML_ind  theorem_i in Proof} starts the proof for the
@@ -1083,6 +1083,7 @@
 done
 
 text {*
+  {\bf TBD below}
 
   (FIXME: read a name and show how to store theorems; see @{ML_ind  note in LocalTheory})
   
--- a/ProgTutorial/ROOT.ML	Thu Oct 01 10:19:21 2009 +0200
+++ b/ProgTutorial/ROOT.ML	Fri Oct 02 15:38:14 2009 +0200
@@ -20,8 +20,6 @@
 use_thy "Recipes/Antiquotes";
 use_thy "Recipes/TimeLimit";
 use_thy "Recipes/Timing";
-use_thy "Recipes/Config";
-use_thy "Recipes/StoringData";
 use_thy "Recipes/ExternalSolver";
 use_thy "Recipes/Oracle";
 use_thy "Recipes/Sat";
--- a/ProgTutorial/Recipes/Config.thy	Thu Oct 01 10:19:21 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-theory Config
-imports "../Base"
-begin
-
-section {* Configuration Options\label{rec:config} *} 
-
-text {*
-  {\bf Problem:} 
-  You would like to enhance your tool with options that can be changed 
-  by the user without having to resort to the ML-level.\smallskip
-
-  {\bf Solution:} This can be achieved using configuration values.\smallskip
-
-  Assume you want to control three values, say @{text bval} containing a
-  boolean,  @{text ival} containing an integer and @{text sval} 
-  containing a string. These values can be declared on the ML-level by
-*}
-
-ML{*val (bval, setup_bval) = Attrib.config_bool "bval" false
-val (ival, setup_ival) = Attrib.config_int "ival" 0
-val (sval, setup_sval) = Attrib.config_string "sval" "some string" *}
-
-text {* 
-  where each value needs to be given a default. To enable these values, they need to 
-  be set up with
-*}
-
-setup {* setup_bval *} 
-setup {* setup_ival *}
-
-text {* or on the ML-level with *}
-
-ML{*setup_sval @{theory} *}
-
-text {*
-  The user can now manipulate the values from within Isabelle with the command
-*}
-
-declare [[bval = true, ival = 3]]
-
-text {*
-  On the ML-level these values can be retrieved using the 
-  function @{ML Config.get}:
-
-  @{ML_response [display,gray] "Config.get @{context} bval" "true"}
-
-  @{ML_response [display,gray] "Config.get @{context} ival" "3"}
-
-  The function @{ML Config.put} manipulates the values. For example
-
-  @{ML_response [display,gray] "Config.put sval \"foo\" @{context}; Config.get @{context} sval" "foo"}
-
-  The same can be achieved using the command \isacommand{setup}.
-*}
-
-setup {* Config.put_thy sval "bar" *}
-
-text {* 
-  Now the retrieval of this value yields:
-
-  @{ML_response [display,gray] "Config.get @{context} sval" "\"bar\""}
-
-  We can apply a function to a value using @{ML Config.map}. For example incrementing
-  @{ML ival} can be done by:
-
-  @{ML_response [display,gray] 
-"let 
-  val ctxt' = Config.map ival (fn i => i + 1) @{context}
-in 
-  Config.get ctxt' ival
-end" "4"}
-
-  \begin{readmore}
-  For more information see @{ML_file "Pure/Isar/attrib.ML"} and @{ML_file "Pure/config.ML"}.
-  \end{readmore}
-
-  There are many good reasons to control parameters in this way. One is
-  that no global reference is needed, which would cause many headaches 
-  with the multithreaded execution of Isabelle.
-*}
-
-end
\ No newline at end of file
--- a/ProgTutorial/Recipes/StoringData.thy	Thu Oct 01 10:19:21 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-theory StoringData
-imports "../Base"
-begin
-
-section {* Storing Data (TBD)\label{rec:storingdata} *} 
-
-
-text {*
-  {\bf Problem:} 
-  Your tool needs to manage data.\smallskip
-
-  {\bf Solution:} This can be achieved using a generic data slot.\smallskip
-
-  Every generic data slot may keep data of any kind which is stored in the
-  context.
-
-  *}
-
-ML{*local
-
-structure Data = GenericDataFun
- ( type T = int Symtab.table
-   val empty = Symtab.empty
-   val extend = I
-   fun merge _ = Symtab.merge (K true)
- )
-
-in
- val lookup = Symtab.lookup o Data.get
- fun update k v = Data.map (Symtab.update (k, v))
-end *}
-
-setup {* Context.theory_map (update "foo" 1) *}
-
-text {*
- 
-  @{ML_response [display,gray] "lookup (Context.Proof @{context}) \"foo\"" "SOME 1"} 
-
-
-*}
-
-text {*
-  alternatives: TheoryDataFun, ProofDataFun
-  Code: Pure/context.ML *}
-
-end
\ No newline at end of file
Binary file progtutorial.pdf has changed