changeset 650:fcf0bd3d1667

BatchG demo
author Adam Chlipala <adamc@hcoop.net>
date Tue, 10 Mar 2009 16:38:38 -0400
parents 96ebc6bdb5a0
children bab524996fca
files demo/batchFun.ur demo/batchFun.urs demo/batchG.ur demo/batchG.urp demo/batchG.urs demo/prose lib/ur/top.ur lib/ur/top.urs src/rpcify.sml
diffstat 9 files changed, 236 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demo/batchFun.ur	Tue Mar 10 16:38:38 2009 -0400
@@ -0,0 +1,162 @@
+con colMeta = fn t_state :: (Type * Type) =>
+                 {Nam : string,
+                  Show : t_state.1 -> xbody,
+                  Inject : sql_injectable t_state.1,
+
+                  NewState : transaction t_state.2,
+                  Widget : t_state.2 -> xbody,
+                  ReadState : t_state.2 -> transaction t_state.1}
+con colsMeta = fn cols :: {(Type * Type)} => $(map colMeta cols)
+
+fun default (t ::: Type) (sh : show t) (rd : read t) (inj : sql_injectable t)
+            name : colMeta (t, source string) =
+    {Nam = name,
+     Show = txt,
+     Inject = _,
+
+     NewState = source "",
+     Widget = fn s => <xml><ctextbox source={s}/></xml>,
+     ReadState = fn s => v <- get s; return (readError v)}
+
+val int = default
+val float = default
+val string = default
+
+functor Make(M : sig
+                 con cols :: {(Type * Type)}
+                 constraint [Id] ~ cols
+                 val fl : folder cols
+                          
+                 val tab : sql_table ([Id = int] ++ map fst cols)
+                           
+                 val title : string
+                             
+                 val cols : colsMeta cols
+             end) = struct
+
+    open constraints M
+    val t = M.tab
+
+    datatype list t = Nil | Cons of t * list t
+
+    fun allRows () =
+        query (SELECT * FROM t)
+              (fn r acc => return (Cons (r.T, acc)))
+              Nil
+
+    fun add r =
+        dml (insert t
+                    (foldR2 [fst] [colMeta]
+                            [fn cols => $(map (fn t :: (Type * Type) =>
+                                                  sql_exp [] [] [] t.1) cols)]
+                            (fn (nm :: Name) (t :: (Type * Type)) (rest :: {(Type * Type)})
+                                             [[nm] ~ rest] input col acc =>
+                                acc ++ {nm = @sql_inject col.Inject input})
+                            {} [M.cols] M.fl (r -- #Id) M.cols
+                            ++ {Id = (SQL {[r.Id]})}))
+
+    fun doBatch ls =
+        case ls of
+            Nil => return ()
+          | Cons (r, ls') =>
+            add r;
+            doBatch ls'
+
+    fun del id =
+        dml (DELETE FROM t WHERE t.Id = {[id]})
+
+    fun show withDel lss =
+        let
+            fun show' ls =
+                case ls of
+                    Nil => <xml/>
+                  | Cons (r, ls) => <xml>
+                    <tr>
+                      <td>{[r.Id]}</td>
+                      {foldRX2 [colMeta] [fst] [_]
+                               (fn (nm :: Name) (p :: (Type * Type)) (rest :: {(Type * Type)})
+                                                [[nm] ~ rest] m v =>
+                                   <xml><td>{m.Show v}</td></xml>)
+                               [M.cols] M.fl M.cols (r -- #Id)}
+                      {if withDel then
+                           <xml><td><button value="Delete" onclick={del r.Id}/></td></xml>
+                       else
+                           <xml/>}
+                    </tr>
+                    {show' ls}
+                  </xml>
+        in
+            <xml><dyn signal={ls <- signal lss; return <xml><table>
+              <tr>
+                <th>Id</th>
+                {foldRX [colMeta] [_]
+                        (fn (nm :: Name) (p :: (Type * Type)) (rest :: {(Type * Type)})
+                                         [[nm] ~ rest] m =>
+                            <xml><th>{[m.Nam]}</th></xml>)
+                        [M.cols] M.fl M.cols}
+              </tr>
+              {show' ls}
+            </table></xml>}/></xml>
+        end
+
+    fun main () =
+        lss <- source Nil;
+        batched <- source Nil;
+
+        id <- source "";
+        inps <- foldR [colMeta] [fn r => transaction ($(map snd r))]
+                (fn (nm :: Name) (p :: (Type * Type)) (rest :: {(Type * Type)}) [[nm] ~ rest] m acc =>
+                    s <- m.NewState;
+                    r <- acc;
+                    return ({nm = s} ++ r))
+                (return {})
+                [M.cols] M.fl M.cols;
+
+        let
+            fun add () =
+                id <- get id;
+                vs <- foldR2 [colMeta] [snd] [fn r => transaction ($(map fst r))]
+                             (fn (nm :: Name) (p :: (Type * Type)) (rest :: {(Type * Type)})
+                                              [[nm] ~ rest] m s acc =>
+                                 v <- m.ReadState s;
+                                 r <- acc;
+                                 return ({nm = v} ++ r))
+                             (return {})
+                             [M.cols] M.fl M.cols inps;
+                ls <- get batched;
+
+                set batched (Cons ({Id = readError id} ++ vs, ls))
+
+            fun exec () =
+                ls <- get batched;
+
+                doBatch ls;
+                set batched Nil
+        in
+            return <xml><body>
+              <h2>Rows</h2>
+
+              {show True lss}
+
+              <button value="Update" onclick={ls <- allRows (); set lss ls}/><br/>
+              <br/>
+
+              <h2>Batch new rows to add</h2>
+
+              <table>
+                <tr> <th>Id:</th> <td><ctextbox source={id}/></td> </tr>
+                {foldRX2 [colMeta] [snd] [_]
+                 (fn (nm :: Name) (p :: (Type * Type)) (rest :: {(Type * Type)})
+                                  [[nm] ~ rest] m s =>
+                     <xml><tr> <th>{[m.Nam]}:</th> <td>{m.Widget s}</td> </tr></xml>)
+                 [M.cols] M.fl M.cols inps}
+                <tr> <th/> <td><button value="Batch it" onclick={add ()}/></td> </tr>
+              </table>
+
+              <h2>Already batched:</h2>
+              {show False batched}
+              <button value="Execute" onclick={exec ()}/>
+            </body></xml>
+        end
+
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demo/batchFun.urs	Tue Mar 10 16:38:38 2009 -0400
@@ -0,0 +1,27 @@
+con colMeta = fn t_state :: (Type * Type) =>
+                 {Nam : string,
+                  Show : t_state.1 -> xbody,
+                  Inject : sql_injectable t_state.1,
+
+                  NewState : transaction t_state.2,
+                  Widget : t_state.2 -> xbody,
+                  ReadState : t_state.2 -> transaction t_state.1}
+con colsMeta = fn cols :: {(Type * Type)} => $(map colMeta cols)
+
+val int : string -> colMeta (int, source string)
+val float : string -> colMeta (float, source string)
+val string : string -> colMeta (string, source string)
+
+functor Make(M : sig
+                 con cols :: {(Type * Type)}
+                 constraint [Id] ~ cols
+                 val fl : folder cols
+
+                 val tab : sql_table ([Id = int] ++ map fst cols)
+
+                 val title : string
+
+                 val cols : colsMeta cols
+             end) : sig
+    val main : unit -> transaction page
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demo/batchG.ur	Tue Mar 10 16:38:38 2009 -0400
@@ -0,0 +1,8 @@
+table t : {Id : int, A : string, B : float}
+
+open BatchFun.Make(struct
+                       val tab = t
+                       val title = "BatchG"
+                       val cols = {A = BatchFun.string "A",
+                                   B = BatchFun.float "B"}
+                   end)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demo/batchG.urp	Tue Mar 10 16:38:38 2009 -0400
@@ -0,0 +1,5 @@
+database dbname=test
+sql batchG.sql
+
+batchFun
+batchG
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demo/batchG.urs	Tue Mar 10 16:38:38 2009 -0400
@@ -0,0 +1,1 @@
+val main : unit -> transaction page
--- a/demo/prose	Tue Mar 10 15:17:23 2009 -0400
+++ b/demo/prose	Tue Mar 10 16:38:38 2009 -0400
@@ -213,3 +213,20 @@
 batch.urp
 
 <p>This example shows more of what is possible with mixed client/server code.  The application is an editor for a simple database table, where additions of new rows can be batched in the client, before a button is clicked to trigger a mass addition.</p>
+
+batchG.urp
+
+<p>We can redo the last example with a generic component, like we did in the <tt>Crud</tt> examples.  The module <tt>BatchFun</tt> is analogous to the <tt>Crud</tt> module.  It contains a functor that builds a batching editor, when given a suitable description of a table.</p>
+
+<p>The signature of the functor is the same as for <tt>Crud</tt>.  We change the definition of <tt>colMeta</tt> to reflect the different kinds of column metadata that we need.  Each column is still described by a pair of types, and the first element of each pair still gives the SQL type for a column.  Now, however, the second type in a pair gives a type of <i>local state</i> to be used in a reactive widget for inputing that column.</p>
+
+<p>The first three fields of a <tt>colMeta</tt> record are the same as for <tt>Crud</tt>.  The rest of the fields are:</p>
+<ol>
+        <li> <tt>NewState</tt>, which allocates some new widget local state</li>
+        <li> <tt>Widget</tt>, which produces a reactive widget from some state</li>
+        <li> <tt>ReadState</tt>, which reads the current value of some state to determine which SQL value it encodes</li>
+</ol>
+
+<p><tt>BatchFun.Make</tt> handles the plumbing of allocating the local state, using it to create widgets, and reading the state values when the user clicks "Batch it."</p>
+
+<p><tt>batchG.ur</tt> contains an example instantiation, which is just as easy to write as in the <tt>Crud1</tt> example.</p>
--- a/lib/ur/top.ur	Tue Mar 10 15:17:23 2009 -0400
+++ b/lib/ur/top.ur	Tue Mar 10 16:38:38 2009 -0400
@@ -7,7 +7,7 @@
                       -> tf [] -> tf r
 
 structure Folder = struct
-    fun fold K (r ::: {K}) (fl : folder r) = fl
+    fun fold K (r :: {K}) (fl : folder r) = fl
 
     fun nil K (tf :: {K} -> Type)
             (f : nm :: Name -> v :: K -> r :: {K} -> tf r
--- a/lib/ur/top.urs	Tue Mar 10 15:17:23 2009 -0400
+++ b/lib/ur/top.urs	Tue Mar 10 16:38:38 2009 -0400
@@ -3,7 +3,7 @@
 con folder :: K --> {K} -> Type
 
 structure Folder : sig
-    val fold : K --> r ::: {K} -> folder r
+    val fold : K --> r :: {K} -> folder r
                -> tf :: ({K} -> Type)
                -> (nm :: Name -> v :: K -> r :: {K} -> tf r
                    -> [[nm] ~ r] => tf ([nm = v] ++ r))
--- a/src/rpcify.sml	Tue Mar 10 15:17:23 2009 -0400
+++ b/src/rpcify.sml	Tue Mar 10 16:38:38 2009 -0400
@@ -71,13 +71,16 @@
 
 fun frob file =
     let
-        fun sideish (basis, ssids) =
-            U.Exp.exists {kind = fn _ => false,
-                          con = fn _ => false,
-                          exp = fn ENamed n => IS.member (ssids, n)
-                                 | EFfi ("Basis", x) => SS.member (basis, x)
-                                 | EFfiApp ("Basis", x, _) => SS.member (basis, x)
-                                 | _ => false}
+        fun sideish (basis, ssids) e =
+            case #1 e of
+                ERecord _ => false
+              | _ =>
+                U.Exp.exists {kind = fn _ => false,
+                              con = fn _ => false,
+                              exp = fn ENamed n => IS.member (ssids, n)
+                                     | EFfi ("Basis", x) => SS.member (basis, x)
+                                     | EFfiApp ("Basis", x, _) => SS.member (basis, x)
+                                     | _ => false} e
 
         fun whichIds basis =
             let
@@ -331,6 +334,10 @@
                                                                                  CorePrint.p_exp CoreEnv.empty (e, loc))];
                                                           raise Fail "Rpcify: Undetected transaction function [2]")
                                                | SOME x => x
+                                                           
+                                         val () = Print.prefaces "Double true"
+                                                                 [("trans1", CorePrint.p_exp CoreEnv.empty trans1),
+                                                                  ("e", CorePrint.p_exp CoreEnv.empty e)]
 
                                          val n' = #maxName st