changeset 1302:d008c4c43a0a

Flex kinds for type-level tuples; ::_ notation
author Adam Chlipala <adam@chlipala.net>
date Sun, 10 Oct 2010 13:07:38 -0400
parents 4359e185d3af
children c7b9a33c26c8
files demo/batchFun.ur demo/crud.ur demo/metaform.ur doc/manual.tex src/elab.sml src/elab_print.sml src/elab_util.sml src/elaborate.sml src/explify.sml src/urweb.grm src/urweb.lex tests/ktuple.ur tests/ktuple.urp
diffstat 13 files changed, 125 insertions(+), 56 deletions(-) [+]
line wrap: on
line diff
--- a/demo/batchFun.ur	Thu Sep 30 18:29:59 2010 -0400
+++ b/demo/batchFun.ur	Sun Oct 10 13:07:38 2010 -0400
@@ -6,7 +6,7 @@
                   NewState : transaction state,
                   Widget : state -> xbody,
                   ReadState : state -> transaction db}
-con colsMeta = fn cols :: {(Type * Type)} => $(map colMeta cols)
+con colsMeta = fn cols => $(map colMeta cols)
 
 fun default [t] (sh : show t) (rd : read t) (inj : sql_injectable t)
             name : colMeta (t, source string) =
@@ -46,10 +46,8 @@
     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 =>
+                      [fn cols => $(map (fn t => sql_exp [] [] [] t.1) cols)]
+                      (fn [nm :: Name] [t ::_] [rest ::_] [[nm] ~ rest] input col acc =>
                           acc ++ {nm = @sql_inject col.Inject input})
                       {} M.fl (r -- #Id) M.cols
                       ++ {Id = (SQL {[r.Id]})}))
@@ -73,8 +71,7 @@
                     <tr>
                       <td>{[r.Id]}</td>
                       {@mapX2 [colMeta] [fst] [_]
-                        (fn [nm :: Name] [p :: (Type * Type)] [rest :: {(Type * Type)}]
-                                         [[nm] ~ rest] m v =>
+                        (fn [nm :: Name] [p ::_] [rest ::_] [[nm] ~ rest] m v =>
                             <xml><td>{m.Show v}</td></xml>)
                         M.fl M.cols (r -- #Id)}
                       {if withDel then
@@ -89,8 +86,7 @@
               <tr>
                 <th>Id</th>
                 {@mapX [colMeta] [_]
-                  (fn [nm :: Name] [p :: (Type * Type)] [rest :: {(Type * Type)}]
-                                   [[nm] ~ rest] m =>
+                  (fn [nm :: Name] [p ::_] [rest ::_] [[nm] ~ rest] m =>
                       <xml><th>{[m.Nam]}</th></xml>)
                   M.fl M.cols}
               </tr>
@@ -104,7 +100,7 @@
 
         id <- source "";
         inps <- @foldR [colMeta] [fn r => transaction ($(map snd r))]
-                 (fn [nm :: Name] [p :: (Type * Type)] [rest :: {(Type * Type)}] [[nm] ~ rest] m acc =>
+                 (fn [nm :: Name] [p ::_] [rest ::_] [[nm] ~ rest] m acc =>
                      s <- m.NewState;
                      r <- acc;
                      return ({nm = s} ++ r))
@@ -115,8 +111,7 @@
             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 =>
+                       (fn [nm :: Name] [p ::_] [rest ::_] [[nm] ~ rest] m s acc =>
                            v <- m.ReadState s;
                            r <- acc;
                            return ({nm = v} ++ r))
@@ -145,8 +140,7 @@
               <table>
                 <tr> <th>Id:</th> <td><ctextbox source={id}/></td> </tr>
                 {@mapX2 [colMeta] [snd] [_]
-                  (fn [nm :: Name] [p :: (Type * Type)] [rest :: {(Type * Type)}]
-                                   [[nm] ~ rest] m s =>
+                  (fn [nm :: Name] [p ::_] [rest ::_] [[nm] ~ rest] m s =>
                       <xml><tr> <th>{[m.Nam]}:</th> <td>{m.Widget s}</td> </tr></xml>)
                   M.fl M.cols inps}
                 <tr> <th/> <td><button value="Batch it" onclick={add ()}/></td> </tr>
--- a/demo/crud.ur	Thu Sep 30 18:29:59 2010 -0400
+++ b/demo/crud.ur	Sun Oct 10 13:07:38 2010 -0400
@@ -5,7 +5,7 @@
                   WidgetPopulated : nm :: Name -> db -> xml form [] [nm = widget],
                   Parse : widget -> db,
                   Inject : sql_injectable db}
-con colsMeta = fn cols :: {(Type * Type)} => $(map colMeta cols)
+con colsMeta = fn cols => $(map colMeta cols)
 
 fun default [t] (sh : show t) (rd : read t) (inj : sql_injectable t)
             name : colMeta (t, string) =
@@ -51,10 +51,9 @@
                          <tr>
                            <td>{[fs.T.Id]}</td>
                            {@mapX2 [fst] [colMeta] [tr]
-                             (fn [nm :: Name] [t :: (Type * Type)] [rest :: {(Type * Type)}]
-                                              [[nm] ~ rest] v col => <xml>
-                                                <td>{col.Show v}</td>
-                                              </xml>)
+                             (fn [nm :: Name] [t ::_] [rest ::_] [[nm] ~ rest] v col => <xml>
+                               <td>{col.Show v}</td>
+                             </xml>)
                              M.fl (fs.T -- #Id) M.cols}
                            <td>
                              <a link={upd fs.T.Id}>[Update]</a>
@@ -67,10 +66,9 @@
             <tr>
               <th>ID</th>
               {@mapX [colMeta] [tr]
-                (fn [nm :: Name] [t :: (Type * Type)] [rest :: {(Type * Type)}]
-                                 [[nm] ~ rest] col => <xml>
-                                   <th>{cdata col.Nam}</th>
-                                 </xml>)
+                (fn [nm :: Name] [t ::_] [rest ::_] [[nm] ~ rest] col => <xml>
+                  <th>{cdata col.Nam}</th>
+                </xml>)
                 M.fl M.cols}
             </tr>
             {rows}
@@ -79,12 +77,11 @@
           <br/><hr/><br/>
 
           <form>
-            {@foldR [colMeta] [fn cols :: {(Type * Type)} => xml form [] (map snd cols)]
-              (fn [nm :: Name] [t :: (Type * Type)] [rest :: {(Type * Type)}]
-                               [[nm] ~ rest] (col : colMeta t) (acc : xml form [] (map snd rest)) => <xml>
-                                 <li> {cdata col.Nam}: {col.Widget [nm]}</li>
-                                 {useMore acc}
-                               </xml>)
+            {@foldR [colMeta] [fn cols => xml form [] (map snd cols)]
+              (fn [nm :: Name] [t ::_] [rest ::_] [[nm] ~ rest] (col : colMeta t) (acc : xml form [] (map snd rest)) => <xml>
+                <li> {cdata col.Nam}: {col.Widget [nm]}</li>
+                {useMore acc}
+              </xml>)
               <xml/>
               M.fl M.cols}
             
@@ -96,10 +93,8 @@
         id <- nextval seq;
         dml (insert tab
                     (@foldR2 [snd] [colMeta]
-                      [fn cols => $(map (fn t :: (Type * Type) =>
-                                            sql_exp [] [] [] t.1) cols)]
-                      (fn [nm :: Name] [t :: (Type * Type)] [rest :: {(Type * Type)}]
-                                       [[nm] ~ rest] =>
+                      [fn cols => $(map (fn t => sql_exp [] [] [] t.1) cols)]
+                      (fn [nm :: Name] [t ::_] [rest ::_] [[nm] ~ rest] =>
                        fn input col acc => acc ++ {nm = @sql_inject col.Inject (col.Parse input)})
                       {} M.fl inputs M.cols
                      ++ {Id = (SQL {[id]})}));
@@ -115,12 +110,8 @@
             fun save (inputs : $(map snd M.cols)) =
                 dml (update [map fst M.cols]
                             (@foldR2 [snd] [colMeta]
-                              [fn cols => $(map (fn t :: (Type * Type) =>
-                                                    sql_exp [T = [Id = int]
-                                                                     ++ map fst M.cols]
-                                                            [] [] t.1) cols)]
-                              (fn [nm :: Name] [t :: (Type * Type)] [rest :: {(Type * Type)}]
-                                               [[nm] ~ rest] =>
+                              [fn cols => $(map (fn t => sql_exp [T = [Id = int] ++ map fst M.cols] [] [] t.1) cols)]
+                              (fn [nm :: Name] [t ::_] [rest ::_] [[nm] ~ rest] =>
                                fn input col acc => acc ++ {nm =
                                                            @sql_inject col.Inject (col.Parse input)})
                               {} M.fl inputs M.cols)
@@ -136,9 +127,8 @@
             case fso : (Basis.option {Tab : $(map fst M.cols)}) of
                 None => return <xml><body>Not found!</body></xml>
               | Some fs => return <xml><body><form>
-                {@foldR2 [fst] [colMeta] [fn cols :: {(Type * Type)} => xml form [] (map snd cols)]
-                  (fn [nm :: Name] [t :: (Type * Type)] [rest :: {(Type * Type)}]
-                                   [[nm] ~ rest] (v : t.1) (col : colMeta t)
+                {@foldR2 [fst] [colMeta] [fn cols => xml form [] (map snd cols)]
+                  (fn [nm :: Name] [t ::_] [rest ::_] [[nm] ~ rest] (v : t.1) (col : colMeta t)
                                    (acc : xml form [] (map snd rest)) =>
                       <xml>
                         <li> {cdata col.Nam}: {col.WidgetPopulated [nm] v}</li>
--- a/demo/metaform.ur	Thu Sep 30 18:29:59 2010 -0400
+++ b/demo/metaform.ur	Sun Oct 10 13:07:38 2010 -0400
@@ -6,7 +6,7 @@
 
     fun handler values = return <xml><body>
       {@mapUX2 [string] [string] [body]
-        (fn [nm :: Name] [rest :: {Unit}] [[nm] ~ rest] name value => <xml>
+        (fn [nm :: Name] [rest ::_] [[nm] ~ rest] name value => <xml>
           <li> {[name]} = {[value]}</li>
         </xml>)
         M.fl M.names values}
@@ -14,8 +14,8 @@
 
     fun main () = return <xml><body>
       <form>
-        {@foldUR [string] [fn cols :: {Unit} => xml form [] (mapU string cols)]
-          (fn [nm :: Name] [rest :: {Unit}] [[nm] ~ rest] name
+        {@foldUR [string] [fn cols => xml form [] (mapU string cols)]
+          (fn [nm :: Name] [rest ::_] [[nm] ~ rest] name
                            (acc : xml form [] (mapU string rest)) => <xml>
                              <li> {[name]}: <textbox{nm}/></li>
                              {useMore acc}
--- a/doc/manual.tex	Thu Sep 30 18:29:59 2010 -0400
+++ b/doc/manual.tex	Sun Oct 10 13:07:38 2010 -0400
@@ -495,6 +495,8 @@
 
 In general, several adjacent $\lambda$ forms may be combined into one, and kind and type annotations may be omitted, in which case they are implicitly included as wildcards.  More formally, for constructor-level abstractions, we can define a new non-terminal $b ::= x \mid (x :: \kappa) \mid X$ and allow composite abstractions of the form $\lambda b^+ \Rightarrow c$, elaborating into the obvious sequence of one core $\lambda$ per element of $b^+$.
 
+In some contexts, the parser isn't happy with token sequences like $x :: \_$, to indicate a constructor variable of wildcard kind.  In such cases, write the second two tokens as $::\hspace{-.05in}\_$, with no intervening spaces.
+
 For any signature item or declaration that defines some entity to be equal to $A$ with classification annotation $B$ (e.g., $\mt{val} \; x : B = A$), $B$ and the preceding colon (or similar punctuation) may be omitted, in which case it is filled in as a wildcard.
 
 A signature item or declaration $\mt{type} \; x$ or $\mt{type} \; x = \tau$ is elaborated into $\mt{con} \; x :: \mt{Type}$ or $\mt{con} \; x :: \mt{Type} = \tau$, respectively.
--- a/src/elab.sml	Thu Sep 30 18:29:59 2010 -0400
+++ b/src/elab.sml	Sun Oct 10 13:07:38 2010 -0400
@@ -39,6 +39,7 @@
 
        | KError
        | KUnif of ErrorMsg.span * string * kind option ref
+       | KTupleUnif of ErrorMsg.span * (int * kind) list * kind option ref
 
        | KRel of int
        | KFun of string * kind
--- a/src/elab_print.sml	Thu Sep 30 18:29:59 2010 -0400
+++ b/src/elab_print.sml	Sun Oct 10 13:07:38 2010 -0400
@@ -56,6 +56,16 @@
       | KError => string "<ERROR>"
       | KUnif (_, _, ref (SOME k)) => p_kind' par env k
       | KUnif (_, s, _) => string ("<UNIF:" ^ s ^ ">")
+      | KTupleUnif (_, _, ref (SOME k)) => p_kind' par env k
+      | KTupleUnif (_, nks, _) => box [string "(",
+                                       p_list_sep (box [space, string "*", space])
+                                                  (fn (n, k) => box [string (Int.toString n ^ ":"),
+                                                                     space,
+                                                                     p_kind env k]) nks,
+                                       space,
+                                       string "*",
+                                       space,
+                                       string "...)"]
 
       | KRel n => ((if !debug then
                          string (E.lookupKRel env n ^ "_" ^ Int.toString n)
--- a/src/elab_util.sml	Thu Sep 30 18:29:59 2010 -0400
+++ b/src/elab_util.sml	Sun Oct 10 13:07:38 2010 -0400
@@ -78,6 +78,16 @@
               | KUnif (_, _, ref (SOME k)) => mfk' ctx k
               | KUnif _ => S.return2 kAll
 
+              | KTupleUnif (_, _, ref (SOME k)) => mfk' ctx k
+              | KTupleUnif (loc, nks, r) =>
+                S.map2 (ListUtil.mapfold (fn (n, k) =>
+                                             S.map2 (mfk ctx k,
+                                                  fn k' =>
+                                                     (n, k'))) nks,
+                     fn nks' =>
+                        (KTupleUnif (loc, nks', r), loc))
+
+
               | KRel _ => S.return2 kAll
               | KFun (x, k) =>
                 S.map2 (mfk (bind (ctx, x)) k,
@@ -207,7 +217,7 @@
               | CError => S.return2 cAll
               | CUnif (_, _, _, ref (SOME c)) => mfc' ctx c
               | CUnif _ => S.return2 cAll
-
+                        
               | CKAbs (x, c) =>
                 S.map2 (mfc (bind (ctx, RelK x)) c,
                         fn c' =>
--- a/src/elaborate.sml	Thu Sep 30 18:29:59 2010 -0400
+++ b/src/elaborate.sml	Sun Oct 10 13:07:38 2010 -0400
@@ -94,6 +94,9 @@
            | (L'.KUnif (_, _, ref (SOME k1All)), _) => unifyKinds' env k1All k2All
            | (_, L'.KUnif (_, _, ref (SOME k2All))) => unifyKinds' env k1All k2All
 
+           | (L'.KTupleUnif (_, _, ref (SOME k)), _) => unifyKinds' env k k2All
+           | (_, L'.KTupleUnif (_, _, ref (SOME k))) => unifyKinds' env k1All k
+
            | (L'.KUnif (_, _, r1), L'.KUnif (_, _, r2)) =>
              if r1 = r2 then
                  ()
@@ -111,6 +114,32 @@
              else
                  r := SOME k1All
 
+           | (L'.KTupleUnif (_, nks, r), L'.KTuple ks) =>
+             ((app (fn (n, k) => unifyKinds' env k (List.nth (ks, n-1))) nks;
+               r := SOME k2All)
+              handle Subscript => err KIncompatible)
+           | (L'.KTuple ks, L'.KTupleUnif (_, nks, r)) =>
+             ((app (fn (n, k) => unifyKinds' env (List.nth (ks, n-1)) k) nks;
+               r := SOME k1All)
+              handle Subscript => err KIncompatible)
+           | (L'.KTupleUnif (loc, nks1, r1), L'.KTupleUnif (_, nks2, r2)) =>
+             let
+                 val nks = foldl (fn (p as (n, k1), nks) =>
+                                     case ListUtil.search (fn (n', k2) =>
+                                                              if n' = n then
+                                                                  SOME k2
+                                                              else
+                                                                  NONE) nks2 of
+                                         NONE => p :: nks
+                                       | SOME k2 => (unifyKinds' env k1 k2;
+                                                     nks)) nks2 nks1
+
+                 val k = (L'.KTupleUnif (loc, nks, ref NONE), loc)
+             in
+                 r1 := SOME k;
+                 r2 := SOME k
+             end
+
            | _ => err KIncompatible
      end
 
@@ -441,16 +470,15 @@
        | L.CProj (c, n) =>
          let
              val (c', k, gs) = elabCon (env, denv) c
+
+             val k' = kunif loc
          in
-             case hnormKind k of
-                 (L'.KTuple ks, _) =>
-                 if n <= 0 orelse n > length ks then
-                     (conError env (ProjBounds (c', n));
-                      (cerror, kerror, []))
-                 else
-                     ((L'.CProj (c', n), loc), List.nth (ks, n - 1), gs)
-               | k => (conError env (ProjMismatch (c', k));
-                       (cerror, kerror, []))
+             if n <= 0 then
+                 (conError env (ProjBounds (c', n));
+                  (cerror, kerror, []))
+             else
+                 (checkKind env c' k (L'.KTupleUnif (loc, [(n, k')], ref NONE), loc);
+                  ((L'.CProj (c', n), loc), k', gs))
          end
 
        | L.CWild k =>
@@ -463,6 +491,7 @@
  fun kunifsRemain k =
      case k of
          L'.KUnif (_, _, ref NONE) => true
+       | L'.KTupleUnif (_, _, ref NONE) => true
        | _ => false
  fun cunifsRemain c =
      case c of
@@ -3229,6 +3258,8 @@
                        | L'.KError => NONE
                        | L'.KUnif (_, _, ref (SOME k)) => decompileKind k
                        | L'.KUnif _ => NONE
+                       | L'.KTupleUnif (_, _, ref (SOME k)) => decompileKind k
+                       | L'.KTupleUnif _ => NONE
 
                        | L'.KRel _ => NONE
                        | L'.KFun _ => NONE
--- a/src/explify.sml	Thu Sep 30 18:29:59 2010 -0400
+++ b/src/explify.sml	Sun Oct 10 13:07:38 2010 -0400
@@ -44,6 +44,8 @@
       | L.KError => raise Fail ("explifyKind: KError at " ^ EM.spanToString loc)
       | L.KUnif (_, _, ref (SOME k)) => explifyKind k
       | L.KUnif _ => raise Fail ("explifyKind: KUnif at " ^ EM.spanToString loc)
+      | L.KTupleUnif (loc, _, ref (SOME k)) => explifyKind k
+      | L.KTupleUnif _ => raise Fail ("explifyKind: KTupleUnif at " ^ EM.spanToString loc)
 
       | L.KRel n => (L'.KRel n, loc)
       | L.KFun (x, k) => (L'.KFun (x, explifyKind k), loc)
--- a/src/urweb.grm	Thu Sep 30 18:29:59 2010 -0400
+++ b/src/urweb.grm	Sun Oct 10 13:07:38 2010 -0400
@@ -212,7 +212,7 @@
  | STRING of string | INT of Int64.int | FLOAT of Real64.real | CHAR of char
  | SYMBOL of string | CSYMBOL of string
  | LPAREN | RPAREN | LBRACK | RBRACK | LBRACE | RBRACE
- | EQ | COMMA | COLON | DCOLON | TCOLON | DOT | HASH | UNDER | UNDERUNDER | BAR
+ | EQ | COMMA | COLON | DCOLON | DCOLONWILD | TCOLON | DOT | HASH | UNDER | UNDERUNDER | BAR
  | PLUS | MINUS | DIVIDE | DOTDOTDOT | MOD | AT
  | CON | LTYPE | VAL | REC | AND | FUN | MAP | UNIT | KUNIT | CLASS
  | DATATYPE | OF
@@ -510,6 +510,7 @@
 
 kopt   :                                (NONE)
        | DCOLON kind                    (SOME kind)
+       | DCOLONWILD                     (SOME (KWild, s (DCOLONWILDleft, DCOLONWILDright)))
 
 dargs  :                                ([])
        | SYMBOL dargs                   (SYMBOL :: dargs)
@@ -853,6 +854,22 @@
                                                 ((CAbs ("_", SOME kind, c), loc),
                                                  (KArrow (kind, k), loc))
                                             end)
+       | SYMBOL DCOLONWILD              (fn (c, k) =>
+                                            let
+                                                val loc = s (SYMBOLleft, DCOLONWILDright)
+                                                val kind = (KWild, loc)
+                                            in
+                                                ((CAbs (SYMBOL, NONE, c), loc),
+                                                 (KArrow (kind, k), loc))
+                                            end)
+       | UNDER DCOLONWILD               (fn (c, k) =>
+                                            let
+                                                val loc = s (UNDERleft, DCOLONWILDright)
+                                                val kind = (KWild, loc)
+                                            in
+                                                ((CAbs ("_", NONE, c), loc),
+                                                 (KArrow (kind, k), loc))
+                                            end)
        | cargp                          (cargp)
 
 cargp  : SYMBOL                         (fn (c, k) =>
@@ -1079,6 +1096,14 @@
                                                  ((ECAbs (Implicit, SYMBOL, kind, e), loc),
                                                   (TCFun (Implicit, SYMBOL, kind, t), loc))
                                              end)
+       | LBRACK SYMBOL DCOLONWILD RBRACK (fn (e, t) =>
+                                             let
+                                                 val loc = s (LBRACKleft, RBRACKright)
+                                                 val kind = (KWild, loc)
+                                             in
+                                                 ((ECAbs (Explicit, SYMBOL, kind, e), loc),
+                                                  (TCFun (Explicit, SYMBOL, kind, t), loc))
+                                             end)
        | LBRACK SYMBOL kcolon kind RBRACK(fn (e, t) =>
                                              let
                                                  val loc = s (LBRACKleft, RBRACKright)
--- a/src/urweb.lex	Thu Sep 30 18:29:59 2010 -0400
+++ b/src/urweb.lex	Sun Oct 10 13:07:38 2010 -0400
@@ -372,6 +372,7 @@
 <INITIAL> ">="        => (Tokens.GE (pos yypos, pos yypos + size yytext));
 <INITIAL> ","         => (Tokens.COMMA (pos yypos, pos yypos + size yytext));
 <INITIAL> ":::"       => (Tokens.TCOLON (pos yypos, pos yypos + size yytext));
+<INITIAL> "::_"       => (Tokens.DCOLONWILD (pos yypos, pos yypos + size yytext));
 <INITIAL> "::"        => (Tokens.DCOLON (pos yypos, pos yypos + size yytext));
 <INITIAL> ":"         => (Tokens.COLON (pos yypos, pos yypos + size yytext));
 <INITIAL> "..."       => (Tokens.DOTDOTDOT (pos yypos, pos yypos + size yytext));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ktuple.ur	Sun Oct 10 13:07:38 2010 -0400
@@ -0,0 +1,2 @@
+type q = (fn p => p.1) (int, float)
+type q = (fn p => p.1 * $p.3) (int, float, [])
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ktuple.urp	Sun Oct 10 13:07:38 2010 -0400
@@ -0,0 +1,1 @@
+ktuple