changeset 944:da3ec6014d2f

Filters implementation type-checking
author Adam Chlipala <adamc@hcoop.net>
date Tue, 15 Sep 2009 15:48:53 -0400
parents e2194a6793ae
children 7710f65935b6
files demo/more/dbgrid.ur demo/more/dbgrid.urs demo/more/dlist.ur demo/more/dlist.urs demo/more/grid.ur demo/more/grid.urp demo/more/grid.urs lib/ur/option.ur lib/ur/option.urs
diffstat 9 files changed, 300 insertions(+), 147 deletions(-) [+]
line wrap: on
line diff
--- a/demo/more/dbgrid.ur	Tue Sep 15 13:07:57 2009 -0400
+++ b/demo/more/dbgrid.ur	Tue Sep 15 15:48:53 2009 -0400
@@ -2,17 +2,20 @@
                  {New : transaction t,
                   Inj : sql_injectable t}
 
-con colMeta' = fn (row :: {Type}) (t :: Type) =>
+con colMeta' = fn (row :: {Type}) (input :: Type) (filter :: Type) =>
                   {Header : string,
-                   Project : $row -> transaction t,
-                   Update : $row -> t -> transaction ($row),
-                   Display : t -> xbody,
-                   Edit : t -> xbody,
-                   Validate : t -> signal bool}
+                   Project : $row -> transaction input,
+                   Update : $row -> input -> transaction ($row),
+                   Display : input -> xbody,
+                   Edit : input -> xbody,
+                   Validate : input -> signal bool,
+                   CreateFilter : transaction filter,
+                   DisplayFilter : filter -> xbody,
+                   Filter : filter -> $row -> signal bool}
 
-con colMeta = fn (row :: {Type}) (global_t :: (Type * Type)) =>
-                 {Initialize : transaction global_t.1,
-                  Handlers : global_t.1 -> colMeta' row global_t.2}
+con colMeta = fn (row :: {Type}) (global_input_filter :: (Type * Type * Type)) =>
+                 {Initialize : transaction global_input_filter.1,
+                  Handlers : global_input_filter.1 -> colMeta' row global_input_filter.2 global_input_filter.3}
 
 con aggregateMeta = fn (row :: {Type}) (acc :: Type) =>
                        {Initial : acc,
@@ -20,22 +23,26 @@
                         Display : acc -> xbody}
 
 structure Direct = struct
-    con metaBase = fn actual_input :: (Type * Type) =>
-                  {Display : actual_input.2 -> xbody,
-                   Edit : actual_input.2 -> xbody,
-                   Initialize : actual_input.1 -> transaction actual_input.2,
-                   Parse : actual_input.2 -> signal (option actual_input.1)}
+    con metaBase = fn actual_input_filter :: (Type * Type * Type) =>
+                  {Display : actual_input_filter.2 -> xbody,
+                   Edit : actual_input_filter.2 -> xbody,
+                   Initialize : actual_input_filter.1 -> transaction actual_input_filter.2,
+                   Parse : actual_input_filter.2 -> signal (option actual_input_filter.1),
+                   CreateFilter : transaction actual_input_filter.3,
+                   DisplayFilter : actual_input_filter.3 -> xbody,
+                   Filter : actual_input_filter.3 -> actual_input_filter.1 -> signal bool}
 
-    datatype metaBoth actual input =
-             NonNull of metaBase (actual, input) * metaBase (option actual, input)
-           | Nullable of metaBase (actual, input)
+    datatype metaBoth actual input filter =
+             NonNull of metaBase (actual, input, filter) * metaBase (option actual, input, filter)
+           | Nullable of metaBase (actual, input, filter)
 
-    con meta = fn global_actual_input :: (Type * Type * Type) =>
-                  {Initialize : transaction global_actual_input.1,
-                   Handlers : global_actual_input.1
-                              -> metaBoth global_actual_input.2 global_actual_input.3}
+    con meta = fn global_actual_input_filter :: (Type * Type * Type * Type) =>
+                  {Initialize : transaction global_actual_input_filter.1,
+                   Handlers : global_actual_input_filter.1
+                              -> metaBoth global_actual_input_filter.2 global_actual_input_filter.3
+                                          global_actual_input_filter.4}
 
-    con editableState (ts :: (Type * Type * Type)) = (ts.1, ts.3)
+    con editableState (ts :: (Type * Type * Type * Type)) = (ts.1, ts.3, ts.4)
     fun editable [ts] [rest] [nm :: Name] [[nm] ~ rest] name (m : meta ts) : colMeta ([nm = ts.2] ++ rest)
                                                                                      (editableState ts) =
        let
@@ -48,7 +55,10 @@
                                                 | Some v => r -- nm ++ {nm = v}),
                           Display = mr.Display,
                           Edit = mr.Edit,
-                          Validate = fn s => vo <- mr.Parse s; return (Option.isSome vo)}
+                          Validate = fn s => vo <- mr.Parse s; return (Option.isSome vo),
+                          CreateFilter = mr.CreateFilter,
+                          DisplayFilter = mr.DisplayFilter,
+                          Filter = fn i r => mr.Filter i r.nm}
        in
            {Initialize = m.Initialize,
             Handlers = fn data => case m.Handlers data of
@@ -56,7 +66,7 @@
                                 | Nullable mr => doMr mr}
        end
         
-    con readOnlyState (ts :: (Type * Type * Type)) = (ts.1, ts.3)
+    con readOnlyState (ts :: (Type * Type * Type * Type)) = (ts.1, ts.3, ts.4)
     fun readOnly [ts] [rest] [nm :: Name] [[nm] ~ rest] name (m : meta ts) : colMeta ([nm = ts.2] ++ rest)
                                                                                      (readOnlyState ts) =
        let
@@ -65,7 +75,10 @@
                           Update = fn r _ => return r,
                           Display = mr.Display,
                           Edit = mr.Display,
-                          Validate = fn _ => return True}
+                          Validate = fn _ => return True,
+                          CreateFilter = mr.CreateFilter,
+                          DisplayFilter = mr.DisplayFilter,
+                          Filter = fn i r => mr.Filter i r.nm}
        in
            {Initialize = m.Initialize,
             Handlers = fn data => case m.Handlers data of
@@ -73,22 +86,30 @@
                                     | Nullable mr => doMr mr}
        end
 
-    con metaBasic = fn actual_input :: (Type * Type) =>
-                       {Display : actual_input.2 -> xbody,
-                        Edit : source actual_input.2 -> xbody,
-                        Initialize : actual_input.1 -> actual_input.2,
-                        InitializeNull : actual_input.2,
-                        IsNull : actual_input.2 -> bool,
-                        Parse : actual_input.2 -> option actual_input.1}
+    con metaBasic = fn actual_input_filter :: (Type * Type * Type) =>
+                       {Display : actual_input_filter.2 -> xbody,
+                        Edit : source actual_input_filter.2 -> xbody,
+                        Initialize : actual_input_filter.1 -> actual_input_filter.2,
+                        InitializeNull : actual_input_filter.2,
+                        IsNull : actual_input_filter.2 -> bool,
+                        Parse : actual_input_filter.2 -> option actual_input_filter.1,
+                        CreateFilter : actual_input_filter.3,
+                        DisplayFilter : source actual_input_filter.3 -> xbody,
+                        Filter : actual_input_filter.3 -> actual_input_filter.1 -> bool,
+                        FilterIsNull : actual_input_filter.3 -> bool}
 
     con basicState = source
-    fun basic [ts ::: (Type * Type)] (m : metaBasic ts) : meta (unit, ts.1, basicState ts.2) =
+    con basicFilter = source
+    fun basic [ts ::: (Type * Type * Type)] (m : metaBasic ts) : meta (unit, ts.1, basicState ts.2, basicFilter ts.3) =
         {Initialize = return (),
          Handlers = fn () => NonNull (
                              {Display = fn s => <xml><dyn signal={v <- signal s; return (m.Display v)}/></xml>,
                               Edit = m.Edit,
                               Initialize = fn v => source (m.Initialize v),
-                              Parse = fn s => v <- signal s; return (m.Parse v)},
+                              Parse = fn s => v <- signal s; return (m.Parse v),
+                              CreateFilter = source m.CreateFilter,
+                              DisplayFilter = m.DisplayFilter,
+                              Filter = fn f v => f <- signal f; return (m.Filter f v)},
                              {Display = fn s => <xml><dyn signal={v <- signal s; return (m.Display v)}/></xml>,
                               Edit = m.Edit,
                               Initialize = fn v => source (case v of
@@ -100,9 +121,18 @@
                                                  else
                                                      case m.Parse v of
                                                          None => None
-                                                       | Some v' => Some (Some v'))})}
+                                                       | Some v' => Some (Some v')),
+                              CreateFilter = source m.CreateFilter,
+                              DisplayFilter = m.DisplayFilter,
+                              Filter = fn f v => f <- signal f;
+                                          return (if m.FilterIsNull f then
+                                                      Option.isNone v
+                                                  else
+                                                      case v of
+                                                          None => False
+                                                        | Some v => m.Filter f v) : signal bool})}
 
-    fun nullable [global] [actual] [input] (m : meta (global, actual, input)) =
+    fun nullable [global] [actual] [input] [filter] (m : meta (global, actual, input, filter)) =
         {Initialize = m.Initialize,
          Handlers = fn d => case m.Handlers d of
                                 Nullable _ => error <xml>Don't stack calls to Direct.nullable!</xml>
@@ -110,33 +140,62 @@
 
     type intGlobal = unit
     type intInput = basicState string
-    val int : meta (intGlobal, int, intInput) =
+    type intFilter = basicFilter string
+    val int : meta (intGlobal, int, intInput, intFilter) =
         basic {Display = fn s => <xml>{[s]}</xml>,
                Edit = fn s => <xml><ctextbox source={s}/></xml>,
                Initialize = fn n => show n,
                InitializeNull = "",
                IsNull = eq "",
-               Parse = fn v => read v}
+               Parse = fn v => read v,
+               CreateFilter = "",
+               DisplayFilter = fn s => <xml><ctextbox source={s}/></xml> : xbody,
+               Filter = fn s n =>
+                           case read s of
+                               None => True
+                             | Some n' => n' = n,
+               FilterIsNull = eq ""}
 
     type stringGlobal = unit
     type stringInput = basicState string
-    val string : meta (stringGlobal, string, stringInput) =
+    type stringFilter = basicFilter string
+    val string : meta (stringGlobal, string, stringInput, stringFilter) =
         basic {Display = fn s => <xml>{[s]}</xml>,
                Edit = fn s => <xml><ctextbox source={s}/></xml>,
                Initialize = fn s => s,
                InitializeNull = "",
                IsNull = eq "",
-               Parse = fn s => Some s}
+               Parse = fn s => Some s,
+               CreateFilter = "",
+               DisplayFilter = fn s => <xml><ctextbox source={s}/></xml> : xbody,
+               Filter = fn s n =>
+                           case read s of
+                               None => True
+                             | Some n' => n' = n,
+               FilterIsNull = eq ""}
 
     type boolGlobal = unit
     type boolInput = basicState bool
-    val bool : meta (boolGlobal, bool, boolInput) =
+    type boolFilter = basicFilter string
+    val bool : meta (boolGlobal, bool, boolInput, boolFilter) =
         basic {Display = fn b => <xml>{[b]}</xml>,
                Edit = fn s => <xml><ccheckbox source={s}/></xml>,
                Initialize = fn b => b,
                InitializeNull = False,
                IsNull = fn _ => False,
-               Parse = fn b => Some b}
+               Parse = fn b => Some b,
+               CreateFilter = "",
+               DisplayFilter = fn s => <xml><cselect source={s}>
+                 <coption/>
+                 <coption value="0">False</coption>
+                 <coption value="1">True</coption>
+               </cselect></xml> : xbody,
+               Filter = fn s b =>
+                           case s of
+                               "0" => b = False
+                             | "1" => b = True
+                             | _ => True,
+               FilterIsNull = eq ""}
 
     functor Foreign (M : sig
                          con row :: {Type}
@@ -152,8 +211,9 @@
                      end) = struct
         open M
 
-        con global = list (t * string)
-        con input = source string * option (t * $row)
+        type global = list (t * string)
+        type input = source string * option (t * $row)
+        type filter = source string
 
         val getChoices = List.mapQuery (SELECT * FROM tab AS T)
                                        (fn r => (r.T.nm, render r.T))
@@ -162,7 +222,7 @@
             r <- oneRow (SELECT T.{{row}} FROM tab AS T WHERE T.{nm} = {[k]});
             return r.T
 
-        val meta : meta (global, M.t, input) =
+        val meta : meta (global, M.t, input, filter) =
             {Initialize = getChoices,
              Handlers = fn choices =>
                            NonNull (
@@ -182,7 +242,19 @@
                             Initialize = fn k => s <- source (show k);
                                             r <- rpc (getChoice k);
                                             return (s, Some (k, r)),
-                            Parse = fn (s, _) => k <- signal s; return (read k : option t)},
+                            Parse = fn (s, _) => k <- signal s; return (read k : option t),
+                            CreateFilter = source "",
+                            DisplayFilter = fn s => 
+                                               <xml><cselect source={s}>
+                                                 <coption/>
+                                                 {List.mapX (fn (k, rend) =>
+                                                                <xml><coption value={show k}>{[rend]}</coption></xml>)
+                                                            choices}
+                                               </cselect></xml> : xbody,
+                            Filter = fn s k => s <- signal s;
+                                        return (case read s : option t of
+                                                    None => True
+                                                  | Some k' => k' = k)},
                            {Display = fn (_, kr) => case kr of
                                                           None => <xml>NULL</xml>
                                                         | Some (k, r) => <xml>{[render ({nm = k} ++ r)]}</xml>,
@@ -212,11 +284,31 @@
                                                    "" => Some None
                                                  | _ => case read ks : option t of
                                                             None => None
-                                                          | Some k => Some (Some k))})}
+                                                          | Some k => Some (Some k)),
+                            CreateFilter = source "",
+                            DisplayFilter = fn s => 
+                                               <xml><cselect source={s}>
+                                                 <coption/>
+                                                 <coption value="0">NULL</coption>
+                                                 {List.mapX (fn (k, rend) =>
+                                                                <xml><coption value={"1" ^ show k}>{[rend]}</coption>
+                                                                </xml>)
+                                                            choices}
+                                               </cselect></xml> : xbody,
+                            Filter = fn s ko => s <- signal s;
+                                        return (case s of
+                                                    "" => True
+                                                  | "0" => ko = None
+                                                  | _ =>
+                                                    case read (String.substring s {Start = 1,
+                                                                                   Len = String.length s - 1})
+                                                         : option t of
+                                                        None => True
+                                                      | Some k => ko = Some k)})}
     end
 end
 
-con computedState = (unit, xbody)
+con computedState = (unit, xbody, unit)
 fun computed [row] [t] (_ : show t) name (f : $row -> t) : colMeta row computedState =
     {Initialize = return (),
      Handlers = fn () => {Header = name,
@@ -224,7 +316,10 @@
                           Update = fn r _ => return r,
                           Display = fn x => x,
                           Edit = fn _ => <xml>...</xml>,
-                          Validate = fn _ => return True}}
+                          Validate = fn _ => return True,
+                          CreateFilter = return (),
+                          DisplayFilter = fn _ => <xml/>,
+                          Filter = fn _ _ => return True}}
 fun computedHtml [row] name (f : $row -> xbody) : colMeta row computedState =
     {Initialize = return (),
      Handlers = fn () => {Header = name,
@@ -232,7 +327,10 @@
                           Update = fn r _ => return r,
                           Display = fn x => x,
                           Edit = fn _ => <xml>...</xml>,
-                          Validate = fn _ => return True}}
+                          Validate = fn _ => return True,
+                          CreateFilter = return (),
+                          DisplayFilter = fn _ => <xml/>,
+                          Filter = fn _ _ => return True}}
 
 functor Make(M : sig
                  con key :: {Type}
@@ -242,7 +340,7 @@
 
                  val raw : $(map rawMeta (key ++ row))
 
-                 con cols :: {(Type * Type)}
+                 con cols :: {(Type * Type * Type)}
                  val cols : $(map (colMeta (key ++ row)) cols)
 
                  val keyFolder : folder key
--- a/demo/more/dbgrid.urs	Tue Sep 15 13:07:57 2009 -0400
+++ b/demo/more/dbgrid.urs	Tue Sep 15 15:48:53 2009 -0400
@@ -2,17 +2,20 @@
                  {New : transaction t,
                   Inj : sql_injectable t}
 
-con colMeta' = fn (row :: {Type}) (t :: Type) =>
+con colMeta' = fn (row :: {Type}) (input :: Type) (filter :: Type) =>
                   {Header : string,
-                   Project : $row -> transaction t,
-                   Update : $row -> t -> transaction ($row),
-                   Display : t -> xbody,
-                   Edit : t -> xbody,
-                   Validate : t -> signal bool}
+                   Project : $row -> transaction input,
+                   Update : $row -> input -> transaction ($row),
+                   Display : input -> xbody,
+                   Edit : input -> xbody,
+                   Validate : input -> signal bool,
+                   CreateFilter : transaction filter,
+                   DisplayFilter : filter -> xbody,
+                   Filter : filter -> $row -> signal bool}
 
-con colMeta = fn (row :: {Type}) (global_t :: (Type * Type)) =>
-                 {Initialize : transaction global_t.1,
-                  Handlers : global_t.1 -> colMeta' row global_t.2}
+con colMeta = fn (row :: {Type}) (global_input_filter :: (Type * Type * Type)) =>
+                 {Initialize : transaction global_input_filter.1,
+                  Handlers : global_input_filter.1 -> colMeta' row global_input_filter.2 global_input_filter.3}
 
 con aggregateMeta = fn (row :: {Type}) (acc :: Type) =>
                        {Initial : acc,
@@ -20,48 +23,55 @@
                         Display : acc -> xbody}
 
 structure Direct : sig
-    con metaBase = fn actual_input :: (Type * Type) =>
-                  {Display : actual_input.2 -> xbody,
-                   Edit : actual_input.2 -> xbody,
-                   Initialize : actual_input.1 -> transaction actual_input.2,
-                   Parse : actual_input.2 -> signal (option actual_input.1)}
+    con metaBase = fn actual_input_filter :: (Type * Type * Type) =>
+                  {Display : actual_input_filter.2 -> xbody,
+                   Edit : actual_input_filter.2 -> xbody,
+                   Initialize : actual_input_filter.1 -> transaction actual_input_filter.2,
+                   Parse : actual_input_filter.2 -> signal (option actual_input_filter.1),
+                   CreateFilter : transaction actual_input_filter.3,
+                   DisplayFilter : actual_input_filter.3 -> xbody,
+                   Filter : actual_input_filter.3 -> actual_input_filter.1 -> signal bool}
 
-    datatype metaBoth actual input =
-             NonNull of metaBase (actual, input) * metaBase (option actual, input)
-           | Nullable of metaBase (actual, input)
+    datatype metaBoth actual input filter =
+             NonNull of metaBase (actual, input, filter) * metaBase (option actual, input, filter)
+           | Nullable of metaBase (actual, input, filter)
 
-    con meta = fn global_actual_input :: (Type * Type * Type) =>
-                  {Initialize : transaction global_actual_input.1,
-                   Handlers : global_actual_input.1
-                              -> metaBoth global_actual_input.2 global_actual_input.3}
+    con meta = fn global_actual_input_filter :: (Type * Type * Type * Type) =>
+                  {Initialize : transaction global_actual_input_filter.1,
+                   Handlers : global_actual_input_filter.1
+                              -> metaBoth global_actual_input_filter.2 global_actual_input_filter.3
+                                          global_actual_input_filter.4}
 
-    con editableState :: (Type * Type * Type) -> (Type * Type)
-    val editable : ts ::: (Type * Type * Type) -> rest ::: {Type}
+    con editableState :: (Type * Type * Type * Type) -> (Type * Type * Type)
+    val editable : ts ::: (Type * Type * Type * Type) -> rest ::: {Type}
                    -> nm :: Name -> [[nm] ~ rest] => string -> meta ts
                                                      -> colMeta ([nm = ts.2] ++ rest)
                                                                 (editableState ts)
 
-    con readOnlyState :: (Type * Type * Type) -> (Type * Type)
-    val readOnly : ts ::: (Type * Type * Type) -> rest ::: {Type}
+    con readOnlyState :: (Type * Type * Type * Type) -> (Type * Type * Type)
+    val readOnly : ts ::: (Type * Type * Type * Type) -> rest ::: {Type}
                    -> nm :: Name -> [[nm] ~ rest] => string -> meta ts
                                                      -> colMeta ([nm = ts.2] ++ rest)
                                                                 (readOnlyState ts)
 
-    val nullable : global ::: Type -> actual ::: Type -> input ::: Type
-                   -> meta (global, actual, input)
-                   -> meta (global, option actual, input)
+    val nullable : global ::: Type -> actual ::: Type -> input ::: Type -> filter ::: Type
+                   -> meta (global, actual, input, filter)
+                   -> meta (global, option actual, input, filter)
 
     type intGlobal
     type intInput
-    val int : meta (intGlobal, int, intInput)
+    type intFilter
+    val int : meta (intGlobal, int, intInput, intFilter)
 
     type stringGlobal
     type stringInput
-    val string : meta (stringGlobal, string, stringInput)
+    type stringFilter
+    val string : meta (stringGlobal, string, stringInput, stringFilter)
 
     type boolGlobal
     type boolInput
-    val bool : meta (boolGlobal, bool, boolInput)
+    type boolFilter
+    val bool : meta (boolGlobal, bool, boolInput, boolFilter)
 
     functor Foreign (M : sig
                          con row :: {Type}
@@ -75,13 +85,14 @@
                          table tab : ([nm = t] ++ row)
                          val render : $([nm = t] ++ row) -> string
                      end) : sig
-        con global :: Type
-        con input :: Type
-        val meta : meta (global, M.t, input)
+        type global
+        type input
+        type filter
+        val meta : meta (global, M.t, input, filter)
     end
 end
 
-con computedState :: (Type * Type)
+con computedState :: (Type * Type * Type)
 val computed : row ::: {Type} -> t ::: Type -> show t
                -> string -> ($row -> t) -> colMeta row computedState
 val computedHtml : row ::: {Type} -> string -> ($row -> xbody) -> colMeta row computedState
@@ -94,7 +105,7 @@
 
                  val raw : $(map rawMeta (key ++ row))
 
-                 con cols :: {(Type * Type)}
+                 con cols :: {(Type * Type * Type)}
                  val cols : $(map (colMeta (key ++ row)) cols)
 
                  val keyFolder : folder key
--- a/demo/more/dlist.ur	Tue Sep 15 13:07:57 2009 -0400
+++ b/demo/more/dlist.ur	Tue Sep 15 15:48:53 2009 -0400
@@ -6,18 +6,19 @@
          Empty
        | Nonempty of { Head : dlist'' t, Tail : source (source (dlist'' t)) }
 
-con dlist t = source (dlist' t)
+con dlist t = {Filter: t -> signal bool,
+               Elements : source (dlist' t)}
 
 type position = transaction unit
 
 fun headPos [t] (dl : dlist t) =
-    dl' <- get dl;
+    dl' <- get dl.Elements;
     case dl' of
         Nonempty { Head = Cons (_, tl), Tail = tl' } =>
         cur <- get tl;
-        set dl (case cur of
-                    Nil => Empty
-                  | _ => Nonempty {Head = cur, Tail = tl'})
+        set dl.Elements (case cur of
+                             Nil => Empty
+                           | _ => Nonempty {Head = cur, Tail = tl'})
       | _ => return ()
 
 fun tailPos [t] (cur : source (dlist'' t)) new tail =
@@ -28,17 +29,20 @@
         Nil => set tail cur
       | _ => return ()
 
-val create = fn [t] => source Empty
+fun create [t] r =
+    s <- source Empty;
+    return {Filter = r.Filter,
+            Elements = s}
 
-fun clear [t] (s : dlist t) = set s Empty
+fun clear [t] (s : dlist t) = set s.Elements Empty
 
 fun append [t] dl v =
-    dl' <- get dl;
+    dl' <- get dl.Elements;
     case dl' of
         Empty =>
         tl <- source Nil;
         tl' <- source tl;
-        set dl (Nonempty {Head = Cons (v, tl), Tail = tl'});
+        set dl.Elements (Nonempty {Head = Cons (v, tl), Tail = tl'});
         return (headPos dl)
                 
       | Nonempty {Tail = tl, ...} =>
@@ -49,7 +53,7 @@
         return (tailPos cur new tl)
 
 fun render [ctx] [ctx ~ body] [t] f dl = <xml>
-  <dyn signal={dl' <- signal dl;
+  <dyn signal={dl' <- signal dl.Elements;
                return (case dl' of
                            Empty => <xml/>
                          | Nonempty {Head = hd, Tail = tlTop} => 
@@ -63,8 +67,13 @@
                                                          None => headPos dl
                                                        | Some prev => tailPos prev tl tlTop
                                        in
-                                           <xml>{f v pos}<dyn signal={tl' <- signal tl;
-                                                                      return (render' (Some tl) tl')}/></xml>
+                                           <xml><dyn signal={b <- dl.Filter v;
+                                                             return (if b then
+                                                                         f v pos
+                                                                     else
+                                                                         <xml/>)}/>
+                                             <dyn signal={tl' <- signal tl;
+                                                          return (render' (Some tl) tl')}/></xml>
                                        end
                            in
                                render' None hd
@@ -82,7 +91,7 @@
         return (x :: tl)
 
 fun elements [t] (dl : dlist t) =
-    dl' <- signal dl;
+    dl' <- signal dl.Elements;
     case dl' of
         Empty => return []
       | Nonempty {Head = hd, ...} => elements' hd
@@ -98,7 +107,7 @@
                 foldl'' i' dl'
 
         fun foldl' (i : acc) (dl : dlist t) : signal acc =
-            dl <- signal dl;
+            dl <- signal dl.Elements;
             case dl of
                 Empty => return i
               | Nonempty {Head = dl, ...} => foldl'' i dl
--- a/demo/more/dlist.urs	Tue Sep 15 13:07:57 2009 -0400
+++ b/demo/more/dlist.urs	Tue Sep 15 15:48:53 2009 -0400
@@ -1,7 +1,7 @@
 con dlist :: Type -> Type
 type position
 
-val create : t ::: Type -> transaction (dlist t)
+val create : t ::: Type -> {Filter : t -> signal bool} -> transaction (dlist t)
 val clear : t ::: Type -> dlist t -> transaction unit
 val append : t ::: Type -> dlist t -> t -> transaction position
 val delete : position -> transaction unit
--- a/demo/more/grid.ur	Tue Sep 15 13:07:57 2009 -0400
+++ b/demo/more/grid.ur	Tue Sep 15 15:48:53 2009 -0400
@@ -1,14 +1,17 @@
-con colMeta' = fn (row :: Type) (t :: Type) =>
+con colMeta' = fn (row :: Type) (input :: Type) (filter :: Type) =>
                   {Header : string,
-                   Project : row -> transaction t,
-                   Update : row -> t -> transaction row,
-                   Display : t -> xbody,
-                   Edit : t -> xbody,
-                   Validate : t -> signal bool}
-
-con colMeta = fn (row :: Type) (global_t :: (Type * Type)) =>
-                 {Initialize : transaction global_t.1,
-                  Handlers : global_t.1 -> colMeta' row global_t.2}                  
+                   Project : row -> transaction input,
+                   Update : row -> input -> transaction row,
+                   Display : input -> xbody,
+                   Edit : input -> xbody,
+                   Validate : input -> signal bool,
+                   CreateFilter : transaction filter,
+                   DisplayFilter : filter -> xbody,
+                   Filter : filter -> row -> signal bool}
+                  
+con colMeta = fn (row :: Type) (global_input_filter :: (Type * Type * Type)) =>
+                 {Initialize : transaction global_input_filter.1,
+                  Handlers : global_input_filter.1 -> colMeta' row global_input_filter.2 global_input_filter.3}
 
 con aggregateMeta = fn (row :: Type) (acc :: Type) =>
                        {Initial : acc,
@@ -25,7 +28,7 @@
                  val save : key -> row -> transaction unit
                  val delete : key -> transaction unit
 
-                 con cols :: {(Type * Type)}
+                 con cols :: {(Type * Type * Type)}
                  val cols : $(map (colMeta row) cols)
 
                  val folder : folder cols
@@ -40,20 +43,21 @@
     style td
     style agg
 
-    fun make (row : M.row) [t] (m : colMeta' M.row t) : transaction t = m.Project row
+    fun make (row : M.row) [input] [filter] (m : colMeta' M.row input filter) : transaction input = m.Project row
 
-    fun makeAll cols row = @@Monad.exec [transaction] _ [map snd M.cols]
-                               (map2 [fst] [colMeta M.row] [fn p :: (Type * Type) => transaction p.2]
-                                     (fn [p] data meta => make row [_] (meta.Handlers data))
+    fun makeAll cols row = @@Monad.exec [transaction] _ [map snd3 M.cols]
+                               (map2 [fst3] [colMeta M.row] [fn p => transaction (snd3 p)]
+                                     (fn [p] data meta => make row [_] [_] (meta.Handlers data))
                                      [_] M.folder cols M.cols)
                                (@@Folder.mp [_] [_] M.folder)
 
-    type grid = {Cols : $(map fst M.cols),
+    type grid = {Cols : $(map fst3 M.cols),
                  Rows : Dlist.dlist {Row : source M.row,
-                                     Cols : source ($(map snd M.cols)),
+                                     Cols : source ($(map snd3 M.cols)),
                                      Updating : source bool,
                                      Selected : source bool},
-                 Selection : source bool}
+                 Selection : source bool,
+                 Filters : $(map thd3 M.cols)}
 
     fun addRow cols rows row =
         rowS <- source row;
@@ -66,15 +70,29 @@
                                          Updating = ud,
                                          Selected = sd})
 
-    val createMetas = Monad.mapR [colMeta M.row] [fst]
-                           (fn [nm :: Name] [p :: (Type * Type)] meta => meta.Initialize)
-                           [_] M.folder M.cols
+    val grid =
+        cols <- Monad.mapR [colMeta M.row] [fst3]
+                           (fn [nm :: Name] [p :: (Type * Type * Type)] meta => meta.Initialize)
+                           [_] M.folder M.cols;
 
-    val grid =
-        cols <- createMetas;
-        rows <- Dlist.create;
+        filters <- Monad.mapR2 [colMeta M.row] [fst3] [thd3]
+                               (fn [nm :: Name] [p :: (Type * Type * Type)] meta state =>
+                                   (meta.Handlers state).CreateFilter)
+                               [_] M.folder M.cols cols;
+
+        rows <- Dlist.create {Filter = fn all =>
+                                          row <- signal all.Row;
+                                          foldR3 [colMeta M.row] [fst3] [thd3] [fn _ => M.row -> signal bool]
+                                                 (fn [nm :: Name] [p :: (Type * Type * Type)]
+                                                                  [rest :: {(Type * Type * Type)}] [[nm] ~ rest]
+                                                                  meta state filter combinedFilter row =>
+                                                     previous <- combinedFilter row;
+                                                     this <- (meta.Handlers state).Filter filter row;
+                                                     return (previous && this))
+                                                 (fn _ => return True)
+                                                 [_] M.folder M.cols cols filters row};
         sel <- source False;
-        return {Cols = cols, Rows = rows, Selection = sel}
+        return {Cols = cols, Rows = rows, Selection = sel, Filters = filters}
 
     fun sync {Cols = cols, Rows = rows, ...} =
         Dlist.clear rows;
@@ -85,8 +103,8 @@
       <table class={tabl}>
         <tr class={tr}>
           <th/> <th/> <th/>
-          {foldRX2 [fst] [colMeta M.row] [_]
-                   (fn [nm :: Name] [p :: (Type * Type)] [rest :: {(Type * Type)}] [[nm] ~ rest]
+          {foldRX2 [fst3] [colMeta M.row] [_]
+                   (fn [nm :: Name] [p :: (Type * Type * Type)] [rest :: {(Type * Type * Type)}] [[nm] ~ rest]
                                     data (meta : colMeta M.row p) =>
                        <xml><th class={th}>{[(meta.Handlers data).Header]}</th></xml>)
                    [_] M.folder grid.Cols M.cols}
@@ -109,8 +127,8 @@
                                   
                               val save =
                                   cols <- get colsS;
-                                  errors <- Monad.foldR3 [fst] [colMeta M.row] [snd] [fn _ => option string]
-                                                         (fn [nm :: Name] [p :: (Type * Type)] [rest :: {(Type * Type)}]
+                                  errors <- Monad.foldR3 [fst3] [colMeta M.row] [snd3] [fn _ => option string]
+                                                         (fn [nm :: Name] [p :: (Type * Type * Type)] [rest :: {(Type * Type * Type)}]
                                                                           [[nm] ~ rest] data meta v errors =>
                                                              b <- current ((meta.Handlers data).Validate v);
                                                              return (if b then
@@ -128,9 +146,9 @@
                                     | None =>
                                       set ud False;
                                       row <- get rowS;
-                                      row' <- Monad.foldR3 [fst] [colMeta M.row] [snd] [fn _ => M.row]
-                                                           (fn [nm :: Name] [t :: (Type * Type)]
-                                                                            [rest :: {(Type * Type)}]
+                                      row' <- Monad.foldR3 [fst3] [colMeta M.row] [snd3] [fn _ => M.row]
+                                                           (fn [nm :: Name] [t :: (Type * Type * Type)]
+                                                                            [rest :: {(Type * Type * Type)}]
                                                                             [[nm] ~ rest] data meta v row' =>
                                                                (meta.Handlers data).Update row' v)
                                                            row [_] M.folder grid.Cols M.cols cols;
@@ -165,9 +183,9 @@
                                 </td>
 
                                 <dyn signal={cols <- signal colsS;
-                                             return (foldRX3 [fst] [colMeta M.row] [snd] [_]
-                                                             (fn [nm :: Name] [t :: (Type * Type)]
-                                                                              [rest :: {(Type * Type)}]
+                                             return (foldRX3 [fst3] [colMeta M.row] [snd3] [_]
+                                                             (fn [nm :: Name] [t :: (Type * Type * Type)]
+                                                                              [rest :: {(Type * Type * Type)}]
                                                                               [[nm] ~ rest] data meta v =>
                                                                  <xml><td class={td}>
                                                                    <dyn signal={b <- signal ud;
@@ -206,6 +224,13 @@
                                         <xml><td class={agg}>{meta.Display acc}</td></xml>)
                                     [_] M.aggFolder M.aggregates rows}
                          </tr></xml>}/>
+
+              <tr><th colspan={3}>Filters</th>
+                {foldRX3 [colMeta M.row] [fst3] [thd3] [_]
+                 (fn [nm :: Name] [p :: (Type * Type * Type)] [rest :: {(Type * Type * Type)}] [[nm] ~ rest]
+                                  meta state filter => <xml><td>{(meta.Handlers state).DisplayFilter filter}</td></xml>)
+                 [_] M.folder M.cols grid.Cols grid.Filters}
+              </tr>
           </table>
           
           <button value="New row" onclick={row <- rpc M.new;
--- a/demo/more/grid.urp	Tue Sep 15 13:07:57 2009 -0400
+++ b/demo/more/grid.urp	Tue Sep 15 15:48:53 2009 -0400
@@ -2,6 +2,7 @@
 $/option
 $/monad
 $/list
+$/string
 dlist
 grid
 dbgrid
--- a/demo/more/grid.urs	Tue Sep 15 13:07:57 2009 -0400
+++ b/demo/more/grid.urs	Tue Sep 15 15:48:53 2009 -0400
@@ -1,14 +1,17 @@
-con colMeta' = fn (row :: Type) (t :: Type) =>
+con colMeta' = fn (row :: Type) (input :: Type) (filter :: Type) =>
                   {Header : string,
-                   Project : row -> transaction t,
-                   Update : row -> t -> transaction row,
-                   Display : t -> xbody,
-                   Edit : t -> xbody,
-                   Validate : t -> signal bool}
+                   Project : row -> transaction input,
+                   Update : row -> input -> transaction row,
+                   Display : input -> xbody,
+                   Edit : input -> xbody,
+                   Validate : input -> signal bool,
+                   CreateFilter : transaction filter,
+                   DisplayFilter : filter -> xbody,
+                   Filter : filter -> row -> signal bool}
                   
-con colMeta = fn (row :: Type) (global_t :: (Type * Type)) =>
-                 {Initialize : transaction global_t.1,
-                  Handlers : global_t.1 -> colMeta' row global_t.2}
+con colMeta = fn (row :: Type) (global_input_filter :: (Type * Type * Type)) =>
+                 {Initialize : transaction global_input_filter.1,
+                  Handlers : global_input_filter.1 -> colMeta' row global_input_filter.2 global_input_filter.3}
 
 con aggregateMeta = fn (row :: Type) (acc :: Type) =>
                        {Initial : acc,
@@ -25,7 +28,7 @@
                  val save : key -> row -> transaction unit
                  val delete : key -> transaction unit
 
-                 con cols :: {(Type * Type)}
+                 con cols :: {(Type * Type * Type)}
                  val cols : $(map (colMeta row) cols)
 
                  val folder : folder cols
--- a/lib/ur/option.ur	Tue Sep 15 13:07:57 2009 -0400
+++ b/lib/ur/option.ur	Tue Sep 15 15:48:53 2009 -0400
@@ -7,6 +7,11 @@
                | (Some x, Some y) => x = y
                | _ => False)
 
+fun isNone [a] x =
+    case x of
+        None => True
+      | Some _ => False
+
 fun isSome [a] x =
     case x of
         None => False
--- a/lib/ur/option.urs	Tue Sep 15 13:07:57 2009 -0400
+++ b/lib/ur/option.urs	Tue Sep 15 15:48:53 2009 -0400
@@ -2,6 +2,7 @@
 
 val eq : a ::: Type -> eq a -> eq (t a)
 
+val isNone : a ::: Type -> t a -> bool
 val isSome : a ::: Type -> t a -> bool
 
 val mp : a ::: Type -> b ::: Type -> (a -> b) -> t a -> t b