diff options
author | Adam Chlipala <adamc@hcoop.net> | 2008-08-16 14:32:18 -0400 |
---|---|---|
committer | Adam Chlipala <adamc@hcoop.net> | 2008-08-16 14:32:18 -0400 |
commit | b4f1361d2dff2e180e4656efa491b275707cdf02 (patch) | |
tree | dc76b47422a5d2bcdbd78d9b9ab35fe30b4991c8 | |
parent | c8fa648dbc2489ca4a56abbb27d94819fb75b5ec (diff) |
Initial type class support
-rw-r--r-- | src/elab.sml | 2 | ||||
-rw-r--r-- | src/elab_env.sig | 3 | ||||
-rw-r--r-- | src/elab_env.sml | 420 | ||||
-rw-r--r-- | src/elab_print.sml | 10 | ||||
-rw-r--r-- | src/elab_util.sig | 5 | ||||
-rw-r--r-- | src/elab_util.sml | 19 | ||||
-rw-r--r-- | src/elaborate.sml | 235 | ||||
-rw-r--r-- | src/explify.sml | 3 | ||||
-rw-r--r-- | src/lacweb.grm | 22 | ||||
-rw-r--r-- | src/lacweb.lex | 1 | ||||
-rw-r--r-- | src/source.sml | 5 | ||||
-rw-r--r-- | src/source_print.sml | 17 | ||||
-rw-r--r-- | tests/type_class.lac | 9 |
13 files changed, 588 insertions, 163 deletions
diff --git a/src/elab.sml b/src/elab.sml index 9beff0c9..ec8a811d 100644 --- a/src/elab.sml +++ b/src/elab.sml @@ -124,6 +124,8 @@ datatype sgn_item' = | SgiSgn of string * int * sgn | SgiConstraint of con * con | SgiTable of int * string * int * con + | SgiClassAbs of string * int + | SgiClass of string * int * con and sgn' = SgnConst of sgn_item list diff --git a/src/elab_env.sig b/src/elab_env.sig index 6f0febcc..32ba7906 100644 --- a/src/elab_env.sig +++ b/src/elab_env.sig @@ -60,6 +60,9 @@ signature ELAB_ENV = sig val lookupConstructor : env -> string -> (Elab.datatype_kind * int * string list * Elab.con option * int) option + val pushClass : env -> int -> env + val resolveClass : env -> Elab.con -> Elab.exp option + val pushERel : env -> string -> Elab.con -> env val lookupERel : env -> int -> string * Elab.con diff --git a/src/elab_env.sml b/src/elab_env.sml index acd918ec..47868e3c 100644 --- a/src/elab_env.sml +++ b/src/elab_env.sml @@ -31,6 +31,7 @@ open Elab structure U = ElabUtil +structure IS = IntBinarySet structure IM = IntBinaryMap structure SM = BinaryMapFn(struct type ord_key = string @@ -61,6 +62,22 @@ val liftConInCon = val lift = liftConInCon 0 +val liftExpInExp = + U.Exp.mapB {kind = fn k => k, + con = fn _ => fn c => c, + exp = fn bound => fn e => + case e of + ERel xn => + if xn < bound then + e + else + ERel (xn + 1) + | _ => e, + bind = fn (bound, U.Exp.RelE _) => bound + 1 + | (bound, _) => bound} + + +val liftExp = liftExpInExp 0 (* Back to environments *) @@ -75,6 +92,61 @@ datatype 'a var = type datatyp = string list * (string * con option) IM.map +datatype class_name = + ClNamed of int + | ClProj of int * string list * string + +structure CK = struct +type ord_key = class_name +open Order +fun compare x = + case x of + (ClNamed n1, ClNamed n2) => Int.compare (n1, n2) + | (ClNamed _, _) => LESS + | (_, ClNamed _) => GREATER + + | (ClProj (m1, ms1, x1), ClProj (m2, ms2, x2)) => + join (Int.compare (m1, m2), + fn () => join (joinL String.compare (ms1, ms2), + fn () => String.compare (x1, x2))) +end + +structure CM = BinaryMapFn(CK) + +datatype class_key = + CkNamed of int + | CkRel of int + | CkProj of int * string list * string + +structure KK = struct +type ord_key = class_key +open Order +fun compare x = + case x of + (CkNamed n1, CkNamed n2) => Int.compare (n1, n2) + | (CkNamed _, _) => LESS + | (_, CkNamed _) => GREATER + + | (CkRel n1, CkRel n2) => Int.compare (n1, n2) + | (CkRel _, _) => LESS + | (_, CkRel _) => GREATER + + | (CkProj (m1, ms1, x1), CkProj (m2, ms2, x2)) => + join (Int.compare (m1, m2), + fn () => join (joinL String.compare (ms1, ms2), + fn () => String.compare (x1, x2))) +end + +structure KM = BinaryMapFn(KK) + +type class = { + ground : exp KM.map +} + +val empty_class = { + ground = KM.empty +} + type env = { renameC : kind var' SM.map, relC : (string * kind) list, @@ -83,6 +155,8 @@ type env = { datatypes : datatyp IM.map, constructors : (datatype_kind * int * string list * con option * int) SM.map, + classes : class CM.map, + renameE : con var' SM.map, relE : (string * con) list, namedE : (string * con) IM.map, @@ -112,6 +186,8 @@ val empty = { datatypes = IM.empty, constructors = SM.empty, + classes = CM.empty, + renameE = SM.empty, relE = [], namedE = IM.empty, @@ -123,6 +199,12 @@ val empty = { str = IM.empty } +fun liftClassKey ck = + case ck of + CkNamed _ => ck + | CkRel n => CkRel (n + 1) + | CkProj _ => ck + fun pushCRel (env : env) x k = let val renameC = SM.map (fn Rel' (n, k) => Rel' (n+1, k) @@ -135,6 +217,13 @@ fun pushCRel (env : env) x k = datatypes = #datatypes env, constructors = #constructors env, + classes = CM.map (fn class => { + ground = KM.foldli (fn (ck, e, km) => + KM.insert (km, liftClassKey ck, e)) + KM.empty (#ground class) + }) + (#classes env), + renameE = #renameE env, relE = map (fn (x, c) => (x, lift c)) (#relE env), namedE = IM.map (fn (x, c) => (x, lift c)) (#namedE env), @@ -159,6 +248,8 @@ fun pushCNamedAs (env : env) x n k co = datatypes = #datatypes env, constructors = #constructors env, + classes = #classes env, + renameE = #renameE env, relE = #relE env, namedE = #namedE env, @@ -203,6 +294,8 @@ fun pushDatatype (env : env) n xs xncs = SM.insert (cmap, x, (dk, n', xs, to, n))) (#constructors env) xncs, + classes = #classes env, + renameE = #renameE env, relE = #relE env, namedE = #namedE env, @@ -229,10 +322,77 @@ fun lookupConstructor (env : env) s = SM.find (#constructors env, s) fun datatypeArgs (xs, _) = xs fun constructors (_, dt) = IM.foldri (fn (n, (x, to), ls) => (x, n, to) :: ls) [] dt +fun pushClass (env : env) n = + {renameC = #renameC env, + relC = #relC env, + namedC = #namedC env, + + datatypes = #datatypes env, + constructors = #constructors env, + + classes = CM.insert (#classes env, ClNamed n, {ground = KM.empty}), + + renameE = #renameE env, + relE = #relE env, + namedE = #namedE env, + + renameSgn = #renameSgn env, + sgn = #sgn env, + + renameStr = #renameStr env, + str = #str env} + +fun class_name_in (c, _) = + case c of + CNamed n => SOME (ClNamed n) + | CModProj x => SOME (ClProj x) + | _ => NONE + +fun class_key_in (c, _) = + case c of + CRel n => SOME (CkRel n) + | CNamed n => SOME (CkNamed n) + | CModProj x => SOME (CkProj x) + | _ => NONE + +fun class_pair_in (c, _) = + case c of + CApp (f, x) => + (case (class_name_in f, class_key_in x) of + (SOME f, SOME x) => SOME (f, x) + | _ => NONE) + | _ => NONE + +fun resolveClass (env : env) c = + case class_pair_in c of + SOME (f, x) => + (case CM.find (#classes env, f) of + NONE => NONE + | SOME class => + case KM.find (#ground class, x) of + NONE => NONE + | SOME e => SOME e) + | _ => NONE + fun pushERel (env : env) x t = let val renameE = SM.map (fn Rel' (n, t) => Rel' (n+1, t) | x => x) (#renameE env) + + val classes = CM.map (fn class => { + ground = KM.map liftExp (#ground class) + }) (#classes env) + val classes = case class_pair_in t of + NONE => classes + | SOME (f, x) => + let + val class = Option.getOpt (CM.find (classes, f), empty_class) + val class = { + ground = KM.insert (#ground class, x, (ERel 0, #2 t)) + } + in + CM.insert (classes, f, class) + end in {renameC = #renameC env, relC = #relC env, @@ -241,6 +401,8 @@ fun pushERel (env : env) x t = datatypes = #datatypes env, constructors = #constructors env, + classes = classes, + renameE = SM.insert (renameE, x, Rel' (0, t)), relE = (x, t) :: #relE env, namedE = #namedE env, @@ -257,22 +419,39 @@ fun lookupERel (env : env) n = handle Subscript => raise UnboundRel n fun pushENamedAs (env : env) x n t = - {renameC = #renameC env, - relC = #relC env, - namedC = #namedC env, + let + val classes = #classes env + val classes = case class_pair_in t of + NONE => classes + | SOME (f, x) => + let + val class = Option.getOpt (CM.find (classes, f), empty_class) + val class = { + ground = KM.insert (#ground class, x, (ENamed n, #2 t)) + } + in + CM.insert (classes, f, class) + end + in + {renameC = #renameC env, + relC = #relC env, + namedC = #namedC env, - datatypes = #datatypes env, - constructors = #constructors env, + datatypes = #datatypes env, + constructors = #constructors env, - renameE = SM.insert (#renameE env, x, Named' (n, t)), - relE = #relE env, - namedE = IM.insert (#namedE env, n, (x, t)), + classes = classes, - renameSgn = #renameSgn env, - sgn = #sgn env, - - renameStr = #renameStr env, - str = #str env} + renameE = SM.insert (#renameE env, x, Named' (n, t)), + relE = #relE env, + namedE = IM.insert (#namedE env, n, (x, t)), + + renameSgn = #renameSgn env, + sgn = #sgn env, + + renameStr = #renameStr env, + str = #str env} + end fun pushENamed env x t = let @@ -301,6 +480,8 @@ fun pushSgnNamedAs (env : env) x n sgis = datatypes = #datatypes env, constructors = #constructors env, + classes = #classes env, + renameE = #renameE env, relE = #relE env, namedE = #namedE env, @@ -326,32 +507,6 @@ fun lookupSgnNamed (env : env) n = fun lookupSgn (env : env) x = SM.find (#renameSgn env, x) -fun pushStrNamedAs (env : env) x n sgis = - {renameC = #renameC env, - relC = #relC env, - namedC = #namedC env, - - datatypes = #datatypes env, - constructors = #constructors env, - - renameE = #renameE env, - relE = #relE env, - namedE = #namedE env, - - renameSgn = #renameSgn env, - sgn = #sgn env, - - renameStr = SM.insert (#renameStr env, x, (n, sgis)), - str = IM.insert (#str env, n, (x, sgis))} - -fun pushStrNamed env x sgis = - let - val n = !namedCounter - in - namedCounter := n + 1; - (pushStrNamedAs env x n sgis, n) - end - fun lookupStrNamed (env : env) n = case IM.find (#str env, n) of NONE => raise UnboundNamed n @@ -359,57 +514,6 @@ fun lookupStrNamed (env : env) n = fun lookupStr (env : env) x = SM.find (#renameStr env, x) -fun sgiBinds env (sgi, loc) = - case sgi of - SgiConAbs (x, n, k) => pushCNamedAs env x n k NONE - | SgiCon (x, n, k, c) => pushCNamedAs env x n k (SOME c) - | SgiDatatype (x, n, xs, xncs) => - let - val env = pushCNamedAs env x n (KType, loc) NONE - in - foldl (fn ((x', n', to), env) => - let - val t = - case to of - NONE => (CNamed n, loc) - | SOME t => (TFun (t, (CNamed n, loc)), loc) - - val k = (KType, loc) - val t = foldr (fn (x, t) => (TCFun (Explicit, x, k, t), loc)) t xs - in - pushENamedAs env x' n' t - end) - env xncs - end - | SgiDatatypeImp (x, n, m1, ms, x', xs, xncs) => - let - val env = pushCNamedAs env x n (KType, loc) (SOME (CModProj (m1, ms, x'), loc)) - in - foldl (fn ((x', n', to), env) => - let - val t = - case to of - NONE => (CNamed n, loc) - | SOME t => (TFun (t, (CNamed n, loc)), loc) - - val k = (KType, loc) - val t = foldr (fn (x, t) => (TCFun (Explicit, x, k, t), loc)) t xs - in - pushENamedAs env x' n' t - end) - env xncs - end - | SgiVal (x, n, t) => pushENamedAs env x n t - | SgiStr (x, n, sgn) => pushStrNamedAs env x n sgn - | SgiSgn (x, n, sgn) => pushSgnNamedAs env x n sgn - | SgiConstraint _ => env - - | SgiTable (tn, x, n, c) => - let - val t = (CApp ((CModProj (tn, [], "table"), loc), c), loc) - in - pushENamedAs env x n t - end fun sgnSeek f sgis = let @@ -439,6 +543,8 @@ fun sgnSeek f sgis = | SgiStr (x, n, _) => seek (sgis, sgns, IM.insert (strs, n, x), cons) | SgiConstraint _ => seek (sgis, sgns, strs, cons) | SgiTable _ => seek (sgis, sgns, strs, cons) + | SgiClassAbs (x, n) => seek (sgis, sgns, strs, IM.insert (cons, n, x)) + | SgiClass (x, n, _) => seek (sgis, sgns, strs, IM.insert (cons, n, x)) in seek (sgis, IM.empty, IM.empty, IM.empty) end @@ -500,17 +606,24 @@ fun sgnS_sgn (str, (sgns, strs, cons)) sgn = end) | _ => sgn -fun sgnSubCon x = - ElabUtil.Con.map {kind = id, - con = sgnS_con x} - fun sgnSubSgn x = ElabUtil.Sgn.map {kind = id, con = sgnS_con x, sgn_item = id, sgn = sgnS_sgn x} -fun hnormSgn env (all as (sgn, loc)) = + + +and projectSgn env {sgn, str, field} = + case #1 (hnormSgn env sgn) of + SgnConst sgis => + (case sgnSeek (fn SgiSgn (x, _, sgn) => if x = field then SOME sgn else NONE | _ => NONE) sgis of + NONE => NONE + | SOME (sgn, subs) => SOME (sgnSubSgn (str, subs) sgn)) + | SgnError => SOME (SgnError, ErrorMsg.dummySpan) + | _ => NONE + +and hnormSgn env (all as (sgn, loc)) = case sgn of SgnError => all | SgnVar n => hnormSgn env (#2 (lookupSgnNamed env n)) @@ -547,14 +660,117 @@ fun hnormSgn env (all as (sgn, loc)) = end | _ => raise Fail "ElabEnv.hnormSgn: Can't reduce 'where' [2]" -and projectSgn env {sgn, str, field} = +fun enrichClasses env classes (m1, ms) sgn = case #1 (hnormSgn env sgn) of SgnConst sgis => - (case sgnSeek (fn SgiSgn (x, _, sgn) => if x = field then SOME sgn else NONE | _ => NONE) sgis of - NONE => NONE - | SOME (sgn, subs) => SOME (sgnSubSgn (str, subs) sgn)) - | SgnError => SOME (SgnError, ErrorMsg.dummySpan) - | _ => NONE + let + val (classes, _) = + foldl (fn (sgi, (classes, newClasses)) => + let + fun found (x, n) = + (CM.insert (classes, + ClProj (m1, ms, x), + empty_class), + IS.add (newClasses, n)) + in + case #1 sgi of + SgiClassAbs xn => found xn + | SgiClass (x, n, _) => found (x, n) + | _ => (classes, newClasses) + end) + (classes, IS.empty) sgis + in + classes + end + | _ => classes + +fun pushStrNamedAs (env : env) x n sgn = + {renameC = #renameC env, + relC = #relC env, + namedC = #namedC env, + + datatypes = #datatypes env, + constructors = #constructors env, + + classes = enrichClasses env (#classes env) (n, []) sgn, + + renameE = #renameE env, + relE = #relE env, + namedE = #namedE env, + + renameSgn = #renameSgn env, + sgn = #sgn env, + + renameStr = SM.insert (#renameStr env, x, (n, sgn)), + str = IM.insert (#str env, n, (x, sgn))} + +fun pushStrNamed env x sgn = + let + val n = !namedCounter + in + namedCounter := n + 1; + (pushStrNamedAs env x n sgn, n) + end + +fun sgiBinds env (sgi, loc) = + case sgi of + SgiConAbs (x, n, k) => pushCNamedAs env x n k NONE + | SgiCon (x, n, k, c) => pushCNamedAs env x n k (SOME c) + | SgiDatatype (x, n, xs, xncs) => + let + val env = pushCNamedAs env x n (KType, loc) NONE + in + foldl (fn ((x', n', to), env) => + let + val t = + case to of + NONE => (CNamed n, loc) + | SOME t => (TFun (t, (CNamed n, loc)), loc) + + val k = (KType, loc) + val t = foldr (fn (x, t) => (TCFun (Explicit, x, k, t), loc)) t xs + in + pushENamedAs env x' n' t + end) + env xncs + end + | SgiDatatypeImp (x, n, m1, ms, x', xs, xncs) => + let + val env = pushCNamedAs env x n (KType, loc) (SOME (CModProj (m1, ms, x'), loc)) + in + foldl (fn ((x', n', to), env) => + let + val t = + case to of + NONE => (CNamed n, loc) + | SOME t => (TFun (t, (CNamed n, loc)), loc) + + val k = (KType, loc) + val t = foldr (fn (x, t) => (TCFun (Explicit, x, k, t), loc)) t xs + in + pushENamedAs env x' n' t + end) + env xncs + end + | SgiVal (x, n, t) => pushENamedAs env x n t + | SgiStr (x, n, sgn) => pushStrNamedAs env x n sgn + | SgiSgn (x, n, sgn) => pushSgnNamedAs env x n sgn + | SgiConstraint _ => env + + | SgiTable (tn, x, n, c) => + let + val t = (CApp ((CModProj (tn, [], "table"), loc), c), loc) + in + pushENamedAs env x n t + end + + | SgiClassAbs (x, n) => pushCNamedAs env x n (KArrow ((KType, loc), (KType, loc)), loc) NONE + | SgiClass (x, n, c) => pushCNamedAs env x n (KArrow ((KType, loc), (KType, loc)), loc) (SOME c) + + +fun sgnSubCon x = + ElabUtil.Con.map {kind = id, + con = sgnS_con x} fun projectStr env {sgn, str, field} = case #1 (hnormSgn env sgn) of @@ -675,6 +891,8 @@ fun sgnSeekConstraints (str, sgis) = | SgiSgn (x, n, _) => seek (sgis, IM.insert (sgns, n, x), strs, cons, acc) | SgiStr (x, n, _) => seek (sgis, sgns, IM.insert (strs, n, x), cons, acc) | SgiTable _ => seek (sgis, sgns, strs, cons, acc) + | SgiClassAbs (x, n) => seek (sgis, sgns, strs, IM.insert (cons, n, x), acc) + | SgiClass (x, n, _) => seek (sgis, sgns, strs, IM.insert (cons, n, x), acc) in seek (sgis, IM.empty, IM.empty, IM.empty, []) end diff --git a/src/elab_print.sml b/src/elab_print.sml index b67ff7eb..73b91b67 100644 --- a/src/elab_print.sml +++ b/src/elab_print.sml @@ -464,6 +464,16 @@ fun p_sgn_item env (sgi, _) = string ":", space, p_con env c] + | SgiClassAbs (x, n) => box [string "class", + space, + p_named x n] + | SgiClass (x, n, c) => box [string "class", + space, + p_named x n, + space, + string "=", + space, + p_con env c] and p_sgn env (sgn, _) = case sgn of diff --git a/src/elab_util.sig b/src/elab_util.sig index e33e956b..57883073 100644 --- a/src/elab_util.sig +++ b/src/elab_util.sig @@ -75,6 +75,11 @@ structure Exp : sig con : (Elab.con', 'state, 'abort) Search.mapfolder, exp : (Elab.exp', 'state, 'abort) Search.mapfolder} -> (Elab.exp, 'state, 'abort) Search.mapfolder + val mapB : {kind : Elab.kind' -> Elab.kind', + con : 'context -> Elab.con' -> Elab.con', + exp : 'context -> Elab.exp' -> Elab.exp', + bind : 'context * binder -> 'context} + -> 'context -> (Elab.exp -> Elab.exp) val exists : {kind : Elab.kind' -> bool, con : Elab.con' -> bool, exp : Elab.exp' -> bool} -> Elab.exp -> bool diff --git a/src/elab_util.sml b/src/elab_util.sml index b0bca7bf..c3ed2d19 100644 --- a/src/elab_util.sml +++ b/src/elab_util.sml @@ -376,6 +376,14 @@ fun exists {kind, con, exp} k = S.Return _ => true | S.Continue _ => false +fun mapB {kind, con, exp, bind} ctx e = + case mapfoldB {kind = fn k => fn () => S.Continue (kind k, ()), + con = fn ctx => fn c => fn () => S.Continue (con ctx c, ()), + exp = fn ctx => fn e => fn () => S.Continue (exp ctx e, ()), + bind = bind} ctx e () of + S.Continue (e, ()) => e + | S.Return _ => raise Fail "ElabUtil.Exp.mapB: Impossible" + end structure Sgn = struct @@ -455,6 +463,11 @@ fun mapfoldB {kind, con, sgn_item, sgn, bind} = S.map2 (con ctx c, fn c' => (SgiTable (tn, x, n, c'), loc)) + | SgiClassAbs _ => S.return2 siAll + | SgiClass (x, n, c) => + S.map2 (con ctx c, + fn c' => + (SgiClass (x, n, c'), loc)) and sg ctx s acc = S.bindP (sg' ctx s acc, sgn ctx) @@ -478,7 +491,11 @@ fun mapfoldB {kind, con, sgn_item, sgn, bind} = | SgiSgn (x, _, sgn) => bind (ctx, Sgn (x, sgn)) | SgiConstraint _ => ctx - | SgiTable _ => ctx, + | SgiTable _ => ctx + | SgiClassAbs (x, _) => + bind (ctx, NamedC (x, (KArrow ((KType, loc), (KType, loc)), loc))) + | SgiClass (x, _, _) => + bind (ctx, NamedC (x, (KArrow ((KType, loc), (KType, loc)), loc))), sgi ctx si)) ctx sgis, fn sgis' => (SgnConst sgis', loc)) diff --git a/src/elaborate.sml b/src/elaborate.sml index a03904d0..e4369dd4 100644 --- a/src/elaborate.sml +++ b/src/elaborate.sml @@ -985,7 +985,8 @@ datatype exp_error = | PatHasNoArg of ErrorMsg.span | Inexhaustive of ErrorMsg.span | DuplicatePatField of ErrorMsg.span * string - | SqlInfer of ErrorMsg.span * L'.con + | Unresolvable of ErrorMsg.span * L'.con + | OutOfContext of ErrorMsg.span fun expError env err = case err of @@ -1028,9 +1029,11 @@ fun expError env err = ErrorMsg.errorAt loc "Inexhaustive 'case'" | DuplicatePatField (loc, s) => ErrorMsg.errorAt loc ("Duplicate record field " ^ s ^ " in pattern") - | SqlInfer (loc, c) => - (ErrorMsg.errorAt loc "Can't infer SQL-ness of type"; - eprefaces' [("Type", p_con env c)]) + | OutOfContext loc => + ErrorMsg.errorAt loc "Type class wildcard occurs out of context" + | Unresolvable (loc, c) => + (ErrorMsg.errorAt loc "Can't resolve type class instance"; + eprefaces' [("Class constraint", p_con env c)]) fun checkCon (env, denv) e c1 c2 = unifyCons (env, denv) c1 c2 @@ -1419,50 +1422,23 @@ fun elabExp (env, denv) (eAll as (e, loc)) = ((L'.EModProj (n, ms, s), loc), t, []) end) - | L.EApp (e1, (L.ESqlInfer, _)) => + | L.EApp (e1, (L.EWild, _)) => let val (e1', t1, gs1) = elabExp (env, denv) e1 val (e1', t1, gs2) = elabHead (env, denv) e1' t1 val (t1, gs3) = hnormCon (env, denv) t1 in case t1 of - (L'.TFun ((L'.CApp ((L'.CModProj (basis, [], "sql_type"), _), - t), _), ran), _) => - if basis <> !basis_r then - raise Fail "Bad use of ESqlInfer [1]" - else - let - val (t, gs4) = hnormCon (env, denv) t - - fun error () = expError env (SqlInfer (loc, t)) - in - case t of - (L'.CModProj (basis, [], x), _) => - (if basis <> !basis_r then - error () - else - case x of - "bool" => () - | "int" => () - | "float" => () - | "string" => () - | _ => error (); - ((L'.EApp (e1', (L'.EModProj (basis, [], "sql_" ^ x), loc)), loc), - ran, gs1 @ gs2 @ gs3 @ gs4)) - | (L'.CUnif (_, (L'.KType, _), _, r), _) => - let - val t = (L'.CModProj (basis, [], "int"), loc) - in - r := SOME t; - ((L'.EApp (e1', (L'.EModProj (basis, [], "sql_int"), loc)), loc), - ran, gs1 @ gs2 @ gs3 @ gs4) - end - | _ => (error (); - (eerror, cerror, [])) - end - | _ => raise Fail "Bad use of ESqlInfer [2]" + (L'.TFun (dom, ran), _) => + (case E.resolveClass env dom of + NONE => (expError env (Unresolvable (loc, dom)); + (eerror, cerror, [])) + | SOME pf => ((L'.EApp (e1', pf), loc), ran, gs1 @ gs2 @ gs3)) + | _ => (expError env (OutOfContext loc); + (eerror, cerror, [])) end - | L.ESqlInfer => raise Fail "Bad use of ESqlInfer [3]" + | L.EWild => (expError env (OutOfContext loc); + (eerror, cerror, [])) | L.EApp (e1, e2) => let @@ -1961,6 +1937,26 @@ fun elabSgn_item ((sgi, loc), (env, denv, gs)) = ([(L'.SgiTable (!basis_r, x, n, c'), loc)], (env, denv, gs)) end + | L.SgiClassAbs x => + let + val k = (L'.KArrow ((L'.KType, loc), (L'.KType, loc)), loc) + val (env, n) = E.pushCNamed env x k NONE + val env = E.pushClass env n + in + ([(L'.SgiClassAbs (x, n), loc)], (env, denv, [])) + end + + | L.SgiClass (x, c) => + let + val k = (L'.KArrow ((L'.KType, loc), (L'.KType, loc)), loc) + val (c', ck, gs) = elabCon (env, denv) c + val (env, n) = E.pushCNamed env x k (SOME c') + val env = E.pushClass env n + in + checkKind env c' ck k; + ([(L'.SgiClass (x, n, c'), loc)], (env, denv, [])) + end + and elabSgn (env, denv) (sgn, loc) = case sgn of L.SgnConst sgis => @@ -2027,7 +2023,19 @@ and elabSgn (env, denv) (sgn, loc) = sgnError env (DuplicateVal (loc, x)) else (); - (cons, SS.add (vals, x), sgns, strs))) + (cons, SS.add (vals, x), sgns, strs)) + | L'.SgiClassAbs (x, _) => + (if SS.member (cons, x) then + sgnError env (DuplicateCon (loc, x)) + else + (); + (SS.add (cons, x), vals, sgns, strs)) + | L'.SgiClass (x, _, _) => + (if SS.member (cons, x) then + sgnError env (DuplicateCon (loc, x)) + else + (); + (SS.add (cons, x), vals, sgns, strs))) (SS.empty, SS.empty, SS.empty, SS.empty) sgis' in ((L'.SgnConst sgis', loc), gs) @@ -2160,6 +2168,20 @@ fun dopen (env, denv) {str, strs, sgn} = | L'.SgiTable (_, x, n, c) => (L'.DVal (x, n, (L'.CApp (tableOf (), c), loc), (L'.EModProj (str, strs, x), loc)), loc) + | L'.SgiClassAbs (x, n) => + let + val k = (L'.KArrow ((L'.KType, loc), (L'.KType, loc)), loc) + val c = (L'.CModProj (str, strs, x), loc) + in + (L'.DCon (x, n, k, c), loc) + end + | L'.SgiClass (x, n, _) => + let + val k = (L'.KArrow ((L'.KType, loc), (L'.KType, loc)), loc) + val c = (L'.CModProj (str, strs, x), loc) + in + (L'.DCon (x, n, k, c), loc) + end in (d, (E.declBinds env' d, denv')) end) @@ -2283,27 +2305,41 @@ fun subSgn (env, denv) sgn1 (sgn2 as (_, loc2)) = in found (x', n1, k', SOME (L'.CModProj (m1, ms, s), loc)) end + | L'.SgiClassAbs (x', n1) => found (x', n1, + (L'.KArrow ((L'.KType, loc), + (L'.KType, loc)), loc), + NONE) + | L'.SgiClass (x', n1, c) => found (x', n1, + (L'.KArrow ((L'.KType, loc), + (L'.KType, loc)), loc), + SOME c) | _ => NONE end) | L'.SgiCon (x, n2, k2, c2) => seek (fn sgi1All as (sgi1, _) => - case sgi1 of - L'.SgiCon (x', n1, k1, c1) => - if x = x' then - let - fun good () = SOME (E.pushCNamedAs env x n2 k2 (SOME c2), denv) - in - (case unifyCons (env, denv) c1 c2 of - [] => good () - | _ => NONE) - handle CUnify (c1, c2, err) => - (sgnError env (SgiWrongCon (sgi1All, c1, sgi2All, c2, err)); - good ()) - end - else - NONE - | _ => NONE) + let + fun found (x', n1, k1, c1) = + if x = x' then + let + fun good () = SOME (E.pushCNamedAs env x n2 k2 (SOME c2), denv) + in + (case unifyCons (env, denv) c1 c2 of + [] => good () + | _ => NONE) + handle CUnify (c1, c2, err) => + (sgnError env (SgiWrongCon (sgi1All, c1, sgi2All, c2, err)); + good ()) + end + else + NONE + in + case sgi1 of + L'.SgiCon (x', n1, k1, c1) => found (x', n1, k1, c1) + | L'.SgiClass (x', n1, c1) => + found (x', n1, (L'.KArrow ((L'.KType, loc), (L'.KType, loc)), loc), c1) + | _ => NONE + end) | L'.SgiDatatype (x, n2, xs2, xncs2) => seek (fn sgi1All as (sgi1, _) => @@ -2491,6 +2527,54 @@ fun subSgn (env, denv) sgn1 (sgn2 as (_, loc2)) = else NONE | _ => NONE) + + | L'.SgiClassAbs (x, n2) => + seek (fn sgi1All as (sgi1, _) => + let + fun found (x', n1, co) = + if x = x' then + let + val k = (L'.KArrow ((L'.KType, loc), (L'.KType, loc)), loc) + val env = E.pushCNamedAs env x n1 k co + in + SOME (if n1 = n2 then + env + else + E.pushCNamedAs env x n2 k (SOME (L'.CNamed n1, loc2)), + denv) + end + else + NONE + in + case sgi1 of + L'.SgiClassAbs (x', n1) => found (x', n1, NONE) + | L'.SgiClass (x', n1, c) => found (x', n1, SOME c) + | _ => NONE + end) + | L'.SgiClass (x, n2, c2) => + seek (fn sgi1All as (sgi1, _) => + let + val k = (L'.KArrow ((L'.KType, loc), (L'.KType, loc)), loc) + + fun found (x', n1, c1) = + if x = x' then + let + fun good () = SOME (E.pushCNamedAs env x n2 k (SOME c2), denv) + in + (case unifyCons (env, denv) c1 c2 of + [] => good () + | _ => NONE) + handle CUnify (c1, c2, err) => + (sgnError env (SgiWrongCon (sgi1All, c1, sgi2All, c2, err)); + good ()) + end + else + NONE + in + case sgi1 of + L'.SgiClass (x', n1, c1) => found (x', n1, c1) + | _ => NONE + end) end in ignore (foldl folder (env, denv) sgis2) @@ -2849,6 +2933,17 @@ fun elabDecl ((d, loc), (env, denv, gs)) = ([(L'.DTable (!basis_r, x, n, c'), loc)], (env, denv, gs' @ gs)) end + | L.DClass (x, c) => + let + val k = (L'.KArrow ((L'.KType, loc), (L'.KType, loc)), loc) + val (c', ck, gs) = elabCon (env, denv) c + val (env, n) = E.pushCNamed env x k (SOME c') + val env = E.pushClass env n + in + checkKind env c' ck k; + ([(L'.DCon (x, n, k, c'), loc)], (env, denv, [])) + end + and elabStr (env, denv) (str, loc) = case str of L.StrConst ds => @@ -2949,6 +3044,30 @@ and elabStr (env, denv) (str, loc) = (SS.add (vals, x), x) in ((L'.SgiTable (tn, x, n, c), loc) :: sgis, cons, vals, sgns, strs) + end + | L'.SgiClassAbs (x, n) => + let + val k = (L'.KArrow ((L'.KType, loc), (L'.KType, loc)), loc) + + val (cons, x) = + if SS.member (cons, x) then + (cons, "?" ^ x) + else + (SS.add (cons, x), x) + in + ((L'.SgiClassAbs (x, n), loc) :: sgis, cons, vals, sgns, strs) + end + | L'.SgiClass (x, n, c) => + let + val k = (L'.KArrow ((L'.KType, loc), (L'.KType, loc)), loc) + + val (cons, x) = + if SS.member (cons, x) then + (cons, "?" ^ x) + else + (SS.add (cons, x), x) + in + ((L'.SgiClass (x, n, c), loc) :: sgis, cons, vals, sgns, strs) end) ([], SS.empty, SS.empty, SS.empty, SS.empty) sgis diff --git a/src/explify.sml b/src/explify.sml index da338147..50a1851b 100644 --- a/src/explify.sml +++ b/src/explify.sml @@ -128,6 +128,9 @@ fun explifySgi (sgi, loc) = | L.SgiSgn (x, n, sgn) => SOME (L'.SgiSgn (x, n, explifySgn sgn), loc) | L.SgiConstraint _ => NONE | L.SgiTable _ => raise Fail "Explify SgiTable" + | L.SgiClassAbs (x, n) => SOME (L'.SgiConAbs (x, n, (L'.KArrow ((L'.KType, loc), (L'.KType, loc)), loc)), loc) + | L.SgiClass (x, n, c) => SOME (L'.SgiCon (x, n, (L'.KArrow ((L'.KType, loc), (L'.KType, loc)), loc), + explifyCon c), loc) and explifySgn (sgn, loc) = case sgn of diff --git a/src/lacweb.grm b/src/lacweb.grm index 464f5f82..fd24ba31 100644 --- a/src/lacweb.grm +++ b/src/lacweb.grm @@ -89,7 +89,7 @@ fun sql_inject (v, t, loc) = | LPAREN | RPAREN | LBRACK | RBRACK | LBRACE | RBRACE | EQ | COMMA | COLON | DCOLON | TCOLON | DOT | HASH | UNDER | UNDERUNDER | BAR | DIVIDE | GT | DOTDOTDOT - | CON | LTYPE | VAL | REC | AND | FOLD | UNIT | KUNIT + | CON | LTYPE | VAL | REC | AND | FOLD | UNIT | KUNIT | CLASS | DATATYPE | OF | TYPE | NAME | ARROW | LARROW | DARROW | STAR @@ -241,6 +241,14 @@ decl : CON SYMBOL EQ cexp (DCon (SYMBOL, NONE, cexp), s (CONleft, | CONSTRAINT cterm TWIDDLE cterm (DConstraint (cterm1, cterm2), s (CONSTRAINTleft, ctermright)) | EXPORT spath (DExport spath, s (EXPORTleft, spathright)) | TABLE SYMBOL COLON cexp (DTable (SYMBOL, entable cexp), s (TABLEleft, cexpright)) + | CLASS SYMBOL EQ cexp (DClass (SYMBOL, cexp), s (CLASSleft, cexpright)) + | CLASS SYMBOL SYMBOL EQ cexp (let + val loc = s (CLASSleft, cexpright) + val k = (KType, loc) + val c = (CAbs (SYMBOL2, SOME k, cexp), loc) + in + (DClass (SYMBOL1, c), s (CLASSleft, cexpright)) + end) dargs : ([]) | SYMBOL dargs (SYMBOL :: dargs) @@ -299,6 +307,15 @@ sgi : CON SYMBOL DCOLON kind (SgiConAbs (SYMBOL, kind), s (CONleft, k | INCLUDE sgn (SgiInclude sgn, s (INCLUDEleft, sgnright)) | CONSTRAINT cterm TWIDDLE cterm (SgiConstraint (cterm1, cterm2), s (CONSTRAINTleft, ctermright)) | TABLE SYMBOL COLON cexp (SgiTable (SYMBOL, entable cexp), s (TABLEleft, cexpright)) + | CLASS SYMBOL (SgiClassAbs SYMBOL, s (CLASSleft, SYMBOLright)) + | CLASS SYMBOL EQ cexp (SgiClass (SYMBOL, cexp), s (CLASSleft, cexpright)) + | CLASS SYMBOL SYMBOL EQ cexp (let + val loc = s (CLASSleft, cexpright) + val k = (KType, loc) + val c = (CAbs (SYMBOL2, SOME k, cexp), loc) + in + (SgiClass (SYMBOL1, c), s (CLASSleft, cexpright)) + end) sgis : ([]) | sgi sgis (sgi :: sgis) @@ -459,6 +476,7 @@ eterm : LPAREN eexp RPAREN (#1 eexp, s (LPARENleft, RPARENright)) (EPrim (Prim.String ""), s (XML_BEGINleft, XML_ENDright))), s (XML_BEGINleft, XML_ENDright)) | LPAREN query RPAREN (query) + | UNDER (EWild, s (UNDERleft, UNDERright)) idents : ident ([ident]) | ident DOT idents (ident :: idents) @@ -633,7 +651,7 @@ sqlexp : TRUE (sql_inject (EVar (["Basis"], "True"), s (FALSEleft, FALSEright))) | LBRACE eexp RBRACE (sql_inject (#1 eexp, - ESqlInfer, + EWild, s (LBRACEleft, RBRACEright))) wopt : (sql_inject (EVar (["Basis"], "True"), diff --git a/src/lacweb.lex b/src/lacweb.lex index 9e994b27..3d745bf2 100644 --- a/src/lacweb.lex +++ b/src/lacweb.lex @@ -280,6 +280,7 @@ notags = [^<{\n]+; <INITIAL> "constraints"=> (Tokens.CONSTRAINTS (pos yypos, pos yypos + size yytext)); <INITIAL> "export" => (Tokens.EXPORT (pos yypos, pos yypos + size yytext)); <INITIAL> "table" => (Tokens.TABLE (pos yypos, pos yypos + size yytext)); +<INITIAL> "class" => (Tokens.CLASS (pos yypos, pos yypos + size yytext)); <INITIAL> "Type" => (Tokens.TYPE (pos yypos, pos yypos + size yytext)); <INITIAL> "Name" => (Tokens.NAME (pos yypos, pos yypos + size yytext)); diff --git a/src/source.sml b/src/source.sml index 70851c73..f8a12a93 100644 --- a/src/source.sml +++ b/src/source.sml @@ -83,6 +83,8 @@ datatype sgn_item' = | SgiInclude of sgn | SgiConstraint of con * con | SgiTable of string * con + | SgiClassAbs of string + | SgiClass of string * con and sgn' = SgnConst of sgn_item list @@ -119,7 +121,7 @@ datatype exp' = | ECut of exp * con | EFold - | ESqlInfer + | EWild | ECase of exp * (pat * exp) list @@ -139,6 +141,7 @@ datatype decl' = | DOpenConstraints of string * string list | DExport of str | DTable of string * con + | DClass of string * con and str' = StrConst of decl list diff --git a/src/source_print.sml b/src/source_print.sml index ceb331f0..6aaeb402 100644 --- a/src/source_print.sml +++ b/src/source_print.sml @@ -379,6 +379,16 @@ fun p_sgn_item (sgi, _) = string ":", space, p_con c] + | SgiClassAbs x => box [string "class", + space, + string x] + | SgiClass (x, c) => box [string "class", + space, + string x, + space, + string "=", + space, + p_con c] and p_sgn (sgn, _) = case sgn of @@ -531,6 +541,13 @@ fun p_decl ((d, _) : decl) = string ":", space, p_con c] + | DClass (x, c) => box [string "class", + space, + string x, + space, + string "=", + space, + p_con c] and p_str (str, _) = case str of diff --git a/tests/type_class.lac b/tests/type_class.lac new file mode 100644 index 00000000..72baa3b3 --- /dev/null +++ b/tests/type_class.lac @@ -0,0 +1,9 @@ +class default t = t + +val string_default : default string = "" +val int_default : default int = 0 + +val default : t :: Type -> default t -> t = + fn t :: Type => fn d : default t => d +val empty = default [string] _ +val zero = default [int] _ |