diff options
author | 2008-03-28 17:34:57 -0400 | |
---|---|---|
committer | 2008-03-28 17:34:57 -0400 | |
commit | e9b1040a1f27a07afc7b2bf33522b1058163bf2b (patch) | |
tree | 07693c3eaedb3b11fb52fdfa8bcf8cc7c8aaecf8 | |
parent | a6d534b172ccb8eadc24e0e903b196085869800e (diff) |
Fun with records
-rw-r--r-- | src/elab.sml | 3 | ||||
-rw-r--r-- | src/elab_print.sml | 55 | ||||
-rw-r--r-- | src/elab_util.sml | 21 | ||||
-rw-r--r-- | src/elaborate.sml | 306 | ||||
-rw-r--r-- | src/lacweb.grm | 7 | ||||
-rw-r--r-- | src/source.sml | 3 | ||||
-rw-r--r-- | src/source_print.sml | 13 | ||||
-rw-r--r-- | tests/stuff.lac | 20 |
8 files changed, 373 insertions, 55 deletions
diff --git a/src/elab.sml b/src/elab.sml index a289d633..b08d9a6c 100644 --- a/src/elab.sml +++ b/src/elab.sml @@ -72,6 +72,9 @@ datatype exp' = | ECApp of exp * con | ECAbs of explicitness * string * kind * exp + | ERecord of (con * exp) list + | EField of exp * con * { field : con, rest : con } + | EError withtype exp = exp' located diff --git a/src/elab_print.sml b/src/elab_print.sml index 83e8f814..c11b0da1 100644 --- a/src/elab_print.sml +++ b/src/elab_print.sml @@ -115,15 +115,26 @@ fun p_con' par env (c, _) = | CName s => box [string "#", string s] - | CRecord (k, xcs) => parenIf par (box [string "[", - p_list (fn (x, c) => - box [p_con env x, - space, - string "=", - space, - p_con env c]) xcs, - string "]::", - p_kind k]) + | CRecord (k, xcs) => + if !debug then + parenIf par (box [string "[", + p_list (fn (x, c) => + box [p_con env x, + space, + string "=", + space, + p_con env c]) xcs, + string "]::", + p_kind k]) + else + parenIf par (box [string "[", + p_list (fn (x, c) => + box [p_con env x, + space, + string "=", + space, + p_con env c]) xcs, + string "]"]) | CConcat (c1, c2) => parenIf par (box [p_con' true env c1, space, string "++", @@ -181,6 +192,32 @@ fun p_exp' par env (e, _) = space, p_exp (E.pushCRel env x k) e]) + | ERecord xes => box [string "{", + p_list (fn (x, e) => + box [p_con env x, + space, + string "=", + space, + p_exp env e]) xes, + string "}"] + | EField (e, c, {field, rest}) => + if !debug then + box [p_exp' true env e, + string ".", + p_con' true env c, + space, + string "[", + p_con env field, + space, + string " in ", + space, + p_con env rest, + string "]"] + else + box [p_exp' true env e, + string ".", + p_con' true env c] + | EError => string "<ERROR>" and p_exp env = p_exp' false env diff --git a/src/elab_util.sml b/src/elab_util.sml index 2550f638..56be1328 100644 --- a/src/elab_util.sml +++ b/src/elab_util.sml @@ -233,6 +233,27 @@ fun mapfoldB {kind = fk, con = fc, exp = fe, bind} = fn e' => (ECAbs (expl, x, k', e'), loc))) + | ERecord xes => + S.map2 (ListUtil.mapfold (fn (x, e) => + S.bind2 (mfc ctx x, + fn x' => + S.map2 (mfe ctx e, + fn e' => + (x', e')))) + xes, + fn xes' => + (ERecord xes', loc)) + | EField (e, c, {field, rest}) => + S.bind2 (mfe ctx e, + fn e' => + S.bind2 (mfc ctx c, + fn c' => + S.bind2 (mfc ctx field, + fn field' => + S.map2 (mfc ctx rest, + fn rest' => + (EField (e', c', {field = field', rest = rest'}), loc))))) + | EError => S.return2 eAll in mfe diff --git a/src/elaborate.sml b/src/elaborate.sml index 0b705fea..9d672869 100644 --- a/src/elaborate.sml +++ b/src/elaborate.sml @@ -136,6 +136,7 @@ val dummy = ErrorMsg.dummySpan val ktype = (L'.KType, dummy) val kname = (L'.KName, dummy) +val ktype_record = (L'.KRecord ktype, dummy) val cerror = (L'.CError, dummy) val kerror = (L'.KError, dummy) @@ -313,6 +314,8 @@ datatype cunify_error = | COccursCheckFailed of L'.con * L'.con | CIncompatible of L'.con * L'.con | CExplicitness of L'.con * L'.con + | CKindof of L'.con + | CRecordFailure exception CUnify' of cunify_error @@ -335,8 +338,212 @@ fun cunifyError env err = eprefaces "Differing constructor function explicitness" [("Con 1", p_con env c1), ("Con 2", p_con env c2)] + | CKindof c => + eprefaces "Kind unification variable blocks kindof calculation" + [("Con", p_con env c)] + | CRecordFailure => + eprefaces "Can't unify record constructors" [] -fun hnormCon env (cAll as (c, _)) = +exception SynUnif + +val liftConInCon = + U.Con.mapB {kind = fn k => k, + con = fn bound => fn c => + case c of + L'.CRel xn => + if xn < bound then + c + else + L'.CRel (xn + 1) + | L'.CUnif _ => raise SynUnif + | _ => c, + bind = fn (bound, U.Con.Rel _) => bound + 1 + | (bound, _) => bound} + +val subConInCon = + U.Con.mapB {kind = fn k => k, + con = fn (xn, rep) => fn c => + case c of + L'.CRel xn' => + if xn = xn' then + #1 rep + else + c + | L'.CUnif _ => raise SynUnif + | _ => c, + bind = fn ((xn, rep), U.Con.Rel _) => (xn+1, liftConInCon 0 rep) + | (ctx, _) => ctx} + +type record_summary = { + fields : (L'.con * L'.con) list, + unifs : (L'.con * L'.con option ref) list, + others : L'.con list +} + +fun summaryToCon {fields, unifs, others} = + let + val c = (L'.CRecord (ktype, []), dummy) + val c = List.foldr (fn (c', c) => (L'.CConcat (c', c), dummy)) c others + val c = List.foldr (fn ((c', _), c) => (L'.CConcat (c', c), dummy)) c unifs + in + (L'.CConcat ((L'.CRecord (ktype, fields), dummy), c), dummy) + end + +fun p_summary env s = p_con env (summaryToCon s) + +exception CUnify of L'.con * L'.con * cunify_error + +fun hnormKind (kAll as (k, _)) = + case k of + L'.KUnif (_, ref (SOME k)) => hnormKind k + | _ => kAll + +fun kindof env (c, loc) = + case c of + L'.TFun _ => ktype + | L'.TCFun _ => ktype + | L'.TRecord _ => ktype + + | L'.CRel xn => #2 (E.lookupCRel env xn) + | L'.CNamed xn => #2 (E.lookupCNamed env xn) + | L'.CApp (c, _) => + (case #1 (hnormKind (kindof env c)) of + L'.KArrow (_, k) => k + | L'.KError => kerror + | _ => raise CUnify' (CKindof c)) + | L'.CAbs (x, k, c) => (L'.KArrow (k, kindof (E.pushCRel env x k) c), loc) + + | L'.CName _ => kname + + | L'.CRecord (k, _) => (L'.KRecord k, loc) + | L'.CConcat (c, _) => kindof env c + + | L'.CError => kerror + | L'.CUnif (k, _, _) => k + +fun unifyRecordCons env (c1, c2) = + let + val k1 = kindof env c1 + val k2 = kindof env c2 + in + unifyKinds k1 k2; + unifySummaries env (k1, recordSummary env c1, recordSummary env c2) + end + +and recordSummary env c : record_summary = + case hnormCon env c of + (L'.CRecord (_, xcs), _) => {fields = xcs, unifs = [], others = []} + | (L'.CConcat (c1, c2), _) => + let + val s1 = recordSummary env c1 + val s2 = recordSummary env c2 + in + {fields = #fields s1 @ #fields s2, + unifs = #unifs s1 @ #unifs s2, + others = #others s1 @ #others s2} + end + | (L'.CUnif (_, _, ref (SOME c)), _) => recordSummary env c + | c' as (L'.CUnif (_, _, r), _) => {fields = [], unifs = [(c', r)], others = []} + | c' => {fields = [], unifs = [], others = [c']} + +and consEq env (c1, c2) = + (unifyCons env c1 c2; + true) + handle CUnify _ => false + +and unifySummaries env (k, s1 : record_summary, s2 : record_summary) = + let + val () = eprefaces "Summaries" [("#1", p_summary env s1), + ("#2", p_summary env s2)] + + fun eatMatching p (ls1, ls2) = + let + fun em (ls1, ls2, passed1) = + case ls1 of + [] => (rev passed1, ls2) + | h1 :: t1 => + let + fun search (ls2', passed2) = + case ls2' of + [] => em (t1, ls2, h1 :: passed1) + | h2 :: t2 => + if p (h1, h2) then + em (t1, List.revAppend (passed2, t2), passed1) + else + search (t2, h2 :: passed2) + in + search (ls2, []) + end + in + em (ls1, ls2, []) + end + + val (fs1, fs2) = eatMatching (fn ((x1, c1), (x2, c2)) => + if consEq env (x1, x2) then + (unifyCons env c1 c2; + true) + else + false) (#fields s1, #fields s2) + val () = eprefaces "Summaries2" [("#1", p_summary env {fields = fs1, unifs = #unifs s1, others = #others s1}), + ("#2", p_summary env {fields = fs2, unifs = #unifs s2, others = #others s2})] + val (unifs1, unifs2) = eatMatching (fn ((_, r1), (_, r2)) => r1 = r2) (#unifs s1, #unifs s2) + val (others1, others2) = eatMatching (consEq env) (#others s1, #others s2) + + fun unifFields (fs, others, unifs) = + case (fs, others, unifs) of + ([], [], _) => ([], [], unifs) + | (_, _, []) => (fs, others, []) + | (_, _, (_, r) :: rest) => + let + val r' = ref NONE + val cr' = (L'.CUnif (k, "recd", r'), dummy) + + val prefix = case (fs, others) of + ([], other :: others) => + List.foldl (fn (other, c) => + (L'.CConcat (c, other), dummy)) + other others + | (fs, []) => + (L'.CRecord (k, fs), dummy) + | (fs, others) => + List.foldl (fn (other, c) => + (L'.CConcat (c, other), dummy)) + (L'.CRecord (k, fs), dummy) others + in + r := SOME (L'.CConcat (prefix, cr'), dummy); + ([], [], (cr', r') :: rest) + end + + val (fs1, others1, unifs2) = unifFields (fs1, others1, unifs2) + val (fs2, others2, unifs1) = unifFields (fs2, others2, unifs1) + + val clear1 = case (fs1, others1) of + ([], []) => true + | _ => false + val clear2 = case (fs2, others2) of + ([], []) => true + | _ => false + val empty = (L'.CRecord (k, []), dummy) + fun pairOffUnifs (unifs1, unifs2) = + case (unifs1, unifs2) of + ([], _) => + if clear1 then + List.app (fn (_, r) => r := SOME empty) unifs2 + else + raise CUnify' CRecordFailure + | (_, []) => + if clear2 then + List.app (fn (_, r) => r := SOME empty) unifs1 + else + raise CUnify' CRecordFailure + | ((c1, _) :: rest1, (_, r2) :: rest2) => + (r2 := SOME c1; + pairOffUnifs (rest1, rest2)) + in + pairOffUnifs (unifs1, unifs2) + end + +and hnormCon env (cAll as (c, _)) = case c of L'.CUnif (_, _, ref (SOME c)) => hnormCon env c @@ -345,14 +552,29 @@ fun hnormCon env (cAll as (c, _)) = (_, _, SOME c') => hnormCon env c' | _ => cAll) + | L'.CApp (c1, c2) => + (case hnormCon env c1 of + (L'.CAbs (_, _, cb), _) => + ((hnormCon env (subConInCon (0, c2) cb)) + handle SynUnif => cAll) + | _ => cAll) + + | L'.CConcat (c1, c2) => + (case (hnormCon env c1, hnormCon env c2) of + ((L'.CRecord (k, xcs1), loc), (L'.CRecord (_, xcs2), _)) => + (L'.CRecord (k, xcs1 @ xcs2), loc) + | _ => cAll) + | _ => cAll -fun unifyCons' env c1 c2 = +and unifyCons' env c1 c2 = unifyCons'' env (hnormCon env c1) (hnormCon env c2) and unifyCons'' env (c1All as (c1, _)) (c2All as (c2, _)) = let fun err f = raise CUnify' (f (c1All, c2All)) + + fun isRecord () = unifyRecordCons env (c1All, c2All) in case (c1, c2) of (L'.TFun (d1, r1), L'.TFun (d2, r2)) => @@ -390,17 +612,6 @@ and unifyCons'' env (c1All as (c1, _)) (c2All as (c2, _)) = else err CIncompatible - | (L'.CRecord (k1, rs1), L'.CRecord (k2, rs2)) => - (unifyKinds k1 k2; - ((ListPair.appEq (fn ((n1, v1), (n2, v2)) => - (unifyCons' env n1 n2; - unifyCons' env v1 v2)) (rs1, rs2)) - handle ListPair.UnequalLengths => err CIncompatible)) - | (L'.CConcat (d1, r1), L'.CConcat (d2, r2)) => - (unifyCons' env d1 d2; - unifyCons' env r1 r2) - - | (L'.CError, _) => () | (_, L'.CError) => () @@ -425,12 +636,15 @@ and unifyCons'' env (c1All as (c1, _)) (c2All as (c2, _)) = else r := SOME c1All + | (L'.CRecord _, _) => isRecord () + | (_, L'.CRecord _) => isRecord () + | (L'.CConcat _, _) => isRecord () + | (_, L'.CConcat _) => isRecord () + | _ => err CIncompatible end -exception CUnify of L'.con * L'.con * cunify_error - -fun unifyCons env c1 c2 = +and unifyCons env c1 c2 = unifyCons' env c1 c2 handle CUnify' err => raise CUnify (c1, c2, err) | KUnify args => raise CUnify (c1, c2, CKind args) @@ -464,36 +678,6 @@ fun checkCon env e c1 c2 = handle CUnify (c1, c2, err) => expError env (Unify (e, c1, c2, err)) -exception SynUnif - -val liftConInCon = - U.Con.mapB {kind = fn k => k, - con = fn bound => fn c => - case c of - L'.CRel xn => - if xn < bound then - c - else - L'.CRel (xn + 1) - | L'.CUnif _ => raise SynUnif - | _ => c, - bind = fn (bound, U.Con.Rel _) => bound + 1 - | (bound, _) => bound} - -val subConInCon = - U.Con.mapB {kind = fn k => k, - con = fn (xn, rep) => fn c => - case c of - L'.CRel xn' => - if xn = xn' then - #1 rep - else - c - | L'.CUnif _ => raise SynUnif - | _ => c, - bind = fn ((xn, rep), U.Con.Rel _) => (xn+1, liftConInCon 0 rep) - | (ctx, _) => ctx} - fun elabExp env (e, loc) = case e of L.EAnnot (e, t) => @@ -576,6 +760,35 @@ fun elabExp env (e, loc) = (L'.TCFun (expl', x, k', et), loc)) end + | L.ERecord xes => + let + val xes' = map (fn (x, e) => + let + val (x', xk) = elabCon env x + val (e', et) = elabExp env e + in + checkKind env x' xk kname; + (x', e', et) + end) xes + in + ((L'.ERecord (map (fn (x', e', _) => (x', e')) xes'), loc), + (L'.TRecord (L'.CRecord (ktype, map (fn (x', _, et) => (x', et)) xes'), loc), loc)) + end + + | L.EField (e, c) => + let + val (e', et) = elabExp env e + val (c', ck) = elabCon env c + + val ft = cunif ktype + val rest = cunif ktype_record + in + checkKind env c' ck kname; + checkCon env e' et (L'.TRecord (L'.CConcat ((L'.CRecord (ktype, [(c', ft)]), loc), rest), loc), loc); + ((L'.EField (e', c', {field = ft, rest = rest}), loc), ft) + end + + datatype decl_error = KunifsRemainKind of ErrorMsg.span * L'.kind | KunifsRemainCon of ErrorMsg.span * L'.con @@ -603,6 +816,7 @@ fun declError env err = fun elabDecl env (d, loc) = (resetKunif (); + resetCunif (); case d of L.DCon (x, ko, c) => let diff --git a/src/lacweb.grm b/src/lacweb.grm index 9d7079e4..80b35510 100644 --- a/src/lacweb.grm +++ b/src/lacweb.grm @@ -62,6 +62,7 @@ val s = ErrorMsg.spanOf | eexp of exp | eapps of exp | eterm of exp + | rexp of (con * exp) list %verbose (* print summary of errors *) %pos int (* positions *) @@ -147,7 +148,13 @@ eexp : eapps (eapps) | FN SYMBOL DARROW eexp (EAbs (SYMBOL, NONE, eexp), s (FNleft, eexpright)) | LPAREN eexp RPAREN DCOLON cexp (EAnnot (eexp, cexp), s (LPARENleft, cexpright)) + | eterm DOT ident (EField (eterm, ident), s (etermleft, identright)) eterm : LPAREN eexp RPAREN (#1 eexp, s (LPARENleft, RPARENright)) | SYMBOL (EVar SYMBOL, s (SYMBOLleft, SYMBOLright)) + | LBRACE rexp RBRACE (ERecord rexp, s (LBRACEleft, RBRACEright)) + +rexp : ([]) + | ident EQ eexp ([(ident, eexp)]) + | ident EQ eexp COMMA rexp ((ident, eexp) :: rexp) diff --git a/src/source.sml b/src/source.sml index d574141c..05b2973f 100644 --- a/src/source.sml +++ b/src/source.sml @@ -68,6 +68,9 @@ datatype exp' = | ECApp of exp * con | ECAbs of explicitness * string * kind * exp + | ERecord of (con * exp) list + | EField of exp * con + withtype exp = exp' located datatype decl' = diff --git a/src/source_print.sml b/src/source_print.sml index b81a82df..de9e2c7c 100644 --- a/src/source_print.sml +++ b/src/source_print.sml @@ -170,6 +170,19 @@ fun p_exp' par (e, _) = space, p_exp e]) + | ERecord xes => box [string "{", + p_list (fn (x, e) => + box [p_con x, + space, + string "=", + space, + p_exp e]) xes, + string "}"] + | EField (e, c) => box [p_exp' true e, + string ".", + p_con' true c] + + and p_exp e = p_exp' false e fun p_decl ((d, _) : decl) = diff --git a/tests/stuff.lac b/tests/stuff.lac index 8348f5a1..806e4dfe 100644 --- a/tests/stuff.lac +++ b/tests/stuff.lac @@ -16,3 +16,23 @@ con c10 = ([]) :: {Type} val v1 = fn t :: Type => fn x : t => x val v2 = v1 [t :: Type -> t -> t] v1 + +val r = {X = v1, Y = v2} +val v1_again = r.X +val v2_again = r.Y + +val r2 = {X = {}, Y = v2, Z = {}} +val r2_X = r2.X +val r2_Y = r2.Y +val r2_Z = r2.Z + +val f = fn fs :: {Type} => fn x : $([X = {}] ++ fs) => x.X +val f2 = fn fs :: {Type} => fn x : $(fs ++ [X = {}]) => x.X +val f3 = fn fs :: {Type} => fn x : $([X = {}, Y = {Z : {}}] ++ fs) => x.X +val f4 = fn fs :: {Type} => fn x : $([X = {}, Y = {Z : {}}] ++ fs) => x.Y +val f5 = fn fs1 :: {Type} => fn fs2 :: {Type} => fn x : $(fs1 ++ [X = {}] ++ fs2) => x.X +val f6 = fn fs1 :: {Type} => fn fs2 :: {Type} => fn x : $(fs1 ++ [X = {}] ++ fs2 ++ [Y = {Z : {}}]) => x.X +val f7 = fn fs1 :: {Type} => fn fs2 :: {Type} => fn x : $(fs1 ++ [X = {}] ++ fs2 ++ [Y = {Z : {}}]) => x.Y + +val test = f [[Y = t :: Type -> t -> t, Z = {}]] r2 +val test = f7 [[Y = t :: Type -> t -> t]] [[Z = {}]] r2 |