|
|
- module M = import "data/map.ml"
- module G = import "./lib/graph.ml"
- open import "./lang.ml"
- open import "amulet/exception.ml"
- open import "prelude.ml"
-
- type tc_tyvar 'a = Tv of {
- name : string, level : int, var : ref (option 'a)
- }
-
- instance eq (tc_tyvar 'a) begin
- let Tv x == Tv y = x.name == y.name
- end
-
- instance ord (tc_tyvar 'a) begin
- let Tv x `compare` Tv y = x.name `compare` y.name
- end
-
- type tc_kappa =
- | K_arr of tc_kappa * tc_kappa
- | K_star
- | K_var of tc_tyvar tc_kappa
-
- type tc_rho =
- | T_uvar of tc_tyvar tc_rho
- | T_var of string
- | T_con of string
- | T_app of tc_rho * tc_rho
- | T_arr of tc_rho * tc_rho
-
- instance show tc_rho begin
- let show =
- let rec show_arg = function
- | T_app _ as x -> "(" ^ go x ^ ")"
- | x -> show_domain x
- and show_domain = function
- | T_arr _ as x -> "(" ^ go x ^ ")"
- | x -> go x
- and go = function
- | T_uvar (Tv n) ->
- match !n.var with
- | Some t -> go t
- | None -> n.name
- | T_var v -> v
- | T_con v -> v
- | T_app (f, x) -> go f ^ " " ^ show_arg x
- | T_arr (a, b) -> show_domain a ^ " -> " ^ go b
- go
- end
-
- instance show tc_kappa begin
- let show x =
- let rec go = function
- | K_star -> "*"
- | K_var (Tv v) -> "?" ^ v.name
- | K_arr (a, b) -> show_domain a ^ " -> " ^ go b
- and show_domain = function
- | K_arr _ as x -> "(" ^ show x ^ ")"
- | x -> go x
- go x
- end
-
- type tc_sigma =
- Forall of {
- vars : list string,
- body : tc_rho
- }
-
- let rec free_unif_vars = function
- | T_uvar v -> S.singleton v
- | T_var _ -> S.empty
- | T_con _ -> S.empty
- | T_app (f, x) -> S.union (free_unif_vars f) (free_unif_vars x)
- | T_arr (a, b) -> S.union (free_unif_vars a) (free_unif_vars b)
-
- let new_name =
- let c = ref 0
- fun () ->
- c := !c + 1
- "alpha" ^ show !c
-
- let new_tcvar level =
- let name = new_name ()
- Tv { name, level, var = ref None }
-
- let rec zonk = function
- | T_uvar (Tv r) as rho ->
- match !r.var with
- | Some rho -> zonk rho
- | None -> rho
- | T_var v -> T_var v
- | T_con v -> T_con v
- | T_app (f, x) -> T_app (zonk f, zonk x)
- | T_arr (f, x) -> T_arr (zonk f, zonk x)
-
- let empty (Tv r) =
- match !r.var with
- | None -> true
- | Some (T_uvar (Tv r')) -> r.name == r'.name
- | _ -> false
-
- let generalise level rho =
- let rho = zonk rho
- let vars =
- free_unif_vars rho
- |> S.filter (fun (Tv r) -> r.level > level && empty (Tv r))
- |> S.members
- flip iter vars @@ fun (Tv r) ->
- r.var := Some (T_var r.name)
- Forall { vars = map (fun (Tv r) -> r.name) vars, body = zonk rho }
-
- let rec unify a b =
- let solve r s =
- match !r.var with
- | Some t -> unify t s
- | None -> r.var := Some s
- match a, b with
- | T_uvar (Tv r), b -> solve r b
- | a, T_uvar (Tv r) -> solve r a
- | T_var a, T_var b when a == b -> ()
- | T_con a, T_con b when a == b -> ()
- | T_app (f, x), T_app (f', x') ->
- unify f f'
- unify x x'
- | T_arr (a, b), T_arr (a', b') ->
- unify a a'
- unify b b'
- | a, b -> error @@ "Types " ^ show a ^ " and " ^ show b ^ " are not equal"
-
- let rec unify_kappa a b =
- let solve r s =
- match !r.var with
- | Some t -> unify_kappa t s
- | None -> r.var := Some s
- match a, b with
- | K_var (Tv r), b -> solve r b
- | a, K_var (Tv r) -> solve r a
- | K_star, K_star -> ()
- | K_arr (a, b), K_arr (a', b') ->
- unify_kappa a a'
- unify_kappa b b'
- | a, b -> error @@ "Kinds " ^ show a ^ " and " ^ show b ^ " are not equal"
-
- type scheme 'a = Poly of tc_sigma | Mono of 'a
-
- instance show 'a => show (scheme 'a) begin
- let show = function
- | Poly (Forall {vars,body}) ->
- foldl (fun s i -> s ^ " " ^ i) "forall" vars ^ ". " ^ show body
- | Mono x -> show x
- end
-
- let mono m = function
- | Mono x -> x
- | Poly _ -> error @@ "Unexpected polytype " ^ m
-
- let get_scope map var =
- match M.lookup var map with
- | Some v -> v
- | None -> error @@ "Name not in scope: " ^ var
-
- let is_function_kind level tau =
- match tau with
- | K_arr (a, b) -> (a, b)
- | _ ->
- let a = new_tcvar level |> K_var
- let b = new_tcvar level |> K_var
- unify_kappa tau (K_arr (a, b))
- (a, b)
-
- let rec infer_kind scope = function
- | Tyvar v ->
- let kappa = get_scope scope v |> mono "(kinds aren't ever polymorphic)"
- (T_var v, kappa)
- | Tycon v ->
- let kappa = get_scope scope v |> mono "(kinds aren't ever polymorphic)"
- (T_con v, kappa)
- | Tyapp (f, x) ->
- let (f, k_f) = infer_kind scope f
- let (x, k_x) = infer_kind scope x
- let (domain, result) = is_function_kind 0 k_f
- unify_kappa domain k_x
- (T_app (f, x), result)
- | Tyarr (a, b) ->
- let a = check_is_type scope a
- let b = check_is_type scope b
- (T_arr (a, b), K_star)
- | Tytup [] -> (T_con "Unit#", K_star)
- | _ -> error "Tuple types not supported"
- and check_is_type scope t =
- let (t, k) = infer_kind scope t
- unify_kappa k K_star
- t
-
- let rec default_to_star = function
- | K_var (Tv r) ->
- match !r.var with
- | Some k -> default_to_star k
- | None -> K_star
- | K_star -> K_star
- | K_arr (a, b) -> K_arr (default_to_star a, default_to_star b)
-
-
- type dt_info <-
- { name : string, d_args : list string, c_args : list tc_rho, c_ret : tc_rho }
-
- let mk_con_info (d_name : string) (d_args : list string) : list (string * list tc_rho) -> list dt_info =
- let go (name, args) =
- { name, c_args = args, d_args, c_ret = foldl (fun f x -> T_app (f, T_var x)) (T_con d_name) d_args }
- map go
-
- let infer_data_group_kind scope (group : list _) =
- let init_kind (group, names) (name, args, constr) =
- let args =
- args |> map (fun v -> (v, new_tcvar 0 |> K_var |> Mono))
- let kind = foldl (fun t (_, r) -> K_arr (t, mono "" r)) K_star args
- let scope = M.from_list args
- ((name, kind, constr, scope, args) :: group, M.insert name (Mono kind) names)
-
- let (group, scope') = foldl init_kind ([], M.empty) group
-
- let scope = M.union scope scope'
-
- let group : list (string * tc_kappa * list string * list (string * list tc_rho)) =
- flip map group @@ fun (name, kind, constrs, args, args') ->
- let scope = M.union scope args
- constrs
- |> map (fun (Constr (name, args)) -> (name, map (check_is_type scope) args))
- |> (name,kind,[x|with (x,_)<-args'],)
-
- flip map group @@ fun (name, kind, args, constrs) ->
- (name, default_to_star kind, constrs, mk_con_info name args constrs)
-
- let rec subst_vars f = function
- | T_var v as t ->
- match f v with
- | None -> t
- | Some t -> t
- | T_uvar (Tv v) as t ->
- match !v.var with
- | Some t -> subst_vars f t
- | None -> t
- | T_con v -> T_con v
- | T_app (a, b) -> T_app (subst_vars f a, subst_vars f b)
- | T_arr (a, b) -> T_arr (subst_vars f a, subst_vars f b)
-
- let instantiate level (Forall { vars, body }) =
- let vars =
- vars
- |> map (fun v -> (v, new_tcvar level |> T_uvar))
- |> M.from_list
- subst_vars (flip M.lookup vars) body
-
- let lookup_ty level scope v =
- get_scope scope v |> function
- | Mono t -> t
- | Poly s -> instantiate level s
-
- let is_function_type level tau =
- match tau with
- | T_arr (a, b) -> (a, b)
- | _ ->
- let a = new_tcvar level |> T_uvar
- let b = new_tcvar level |> T_uvar
- unify tau (T_arr (a, b))
- (a, b)
-
- (* TODO: Rank-N types *)
- let is_subtype = unify
-
- let rec infer dt_info level scope = function
- | Ref v -> lookup_ty level scope v |> (Ref v,)
- | App (f, x) ->
- let (f, arg, res) =
- infer dt_info level scope f
- |> second (is_function_type level)
- let x = check dt_info level scope arg x
- (App (f, x), res)
- | Lit i -> (Lit i, T_con "Int")
- | Let (bindings, body) ->
- let (bindings, scope') =
- infer_binding_group dt_info level scope bindings
- let (body, body_t) = infer dt_info level (scope `M.union` map force scope') body
- (Let (bindings, body), body_t)
- | x ->
- let t = new_tcvar level |> T_uvar
- let x = check dt_info level scope t x
- (x, t)
-
- and check dt_info level scope wanted = function
- | Lam (arg, body) ->
- let (arg_t, body_t) = is_function_type level wanted
- let body =
- (* TODO: Rank-N types *)
- check dt_info level (M.insert arg (Mono arg_t) scope) body_t body
- Lam (arg, body)
- | Case (_, []) -> error "Empty case"
- | Case (scrutinee, Cons ((con, _, _), _) as patterns) ->
- let data =
- match M.lookup con dt_info with
- | Some data -> data
- | None -> error @@ "Constructor " ^ con ^ " doesn't belong to a type"
-
- let (scrutinee, scrut_t) = infer dt_info level scope scrutinee
-
- let go_arm {name, d_args, c_args, c_ret} (con, args, expr) =
- if name <> con then
- error @@ "Constructors out of order: expected this pattern to match " ^ name
- else ()
-
- if length c_args <> length args then
- error @@ "Constructor "
- ^ con ^ " has "
- ^ show (length c_args)
- ^ " but is being matched against with " ^ show (length args)
- ^ " variables"
- else ()
-
- let d_args =
- d_args
- |> map (fun v -> (v, new_tcvar level |> T_uvar))
- |> M.from_list
- let c_args = map (Mono # subst_vars (flip M.lookup d_args)) c_args
- let c_ret = subst_vars (flip M.lookup d_args) c_ret
-
- unify c_ret scrut_t
-
- let scope' = M.from_list (zip args c_args) `M.union` scope
- (con, args, check dt_info level scope' wanted expr)
-
- Case (scrutinee, zip_with go_arm data patterns)
- | x ->
- let (x, t) = infer dt_info level scope x
- is_subtype t wanted
- x
-
- and infer_binding_group dt_info level (scope : M.t string _) bindings : _ * M.t string _ =
- let inner = level + 1
- let initial_types =
- bindings
- |> map (fun (name, _) -> (name, new_tcvar inner |> T_uvar |> Mono))
- |> M.from_list
-
- let initial_types = initial_types |> M.union scope
-
- let go_binding (bindings : list _, scope' : M.t _ _) (name : string, body : expr) =
- let (body, body_ty) =
- (fun () -> infer dt_info inner initial_types body)
- `catch` fun (e : some exception) ->
- error (describe_exception e ^ "\nwhen type checking " ^ name)
- M.lookup name scope
- |> function
- | Some (Mono t) -> unify t body_ty
- | _ -> ()
- (
- (name, body) :: bindings,
- M.insert name (lazy (generalise level body_ty |> Poly)) scope'
- )
- foldl go_binding ([], M.empty) bindings
-
- let dependency_graph defs =
- let rec free_vars_of_cons t m (Constr (name, args)) =
- let cons =
- foldl (fun s t -> S.union s (free_cons t)) (S.singleton t)
- args
- M.insert name cons m
- let define n x m =
- M.alter (function
- | Some _ -> error @@ "Redefinition of value " ^ n
- | None -> Some x)
- n m
- let go (graph, defs) = function
- | Foreign (Fimport { var }) as x ->
- (M.insert var S.empty graph, define var x defs)
- | Decl (name, args, expr) as x ->
- let fvs =
- free_vars expr
- |> flip S.difference (S.from_list args)
- |> S.delete name
- (M.insert name fvs graph, define name x defs)
- | Data (name, _, cons) as x ->
- M.union graph (foldl (free_vars_of_cons name) M.empty cons)
- |> M.insert name S.empty
- |> (, define name x defs)
- let (graph, defs) = foldl go (M.empty, M.empty) defs
- (G.groups_of_sccs graph, defs)
-
- let mk_lam args body = foldr (curry Lam) body args
- let rec unlambda = function
- | Lam (v, x) ->
- let (args, x) = unlambda x
- (v :: args, x)
- | e -> ([], e)
-
- let rec replicate n x =
- if n <= 0 then
- []
- else
- x :: replicate (n - 1) x
-
- let rec add_missing_vars scope = function
- | Tyvar v ->
- match M.lookup v scope with
- | Some _ -> scope
- | None ->
- let k = new_tcvar 0 |> K_var
- M.insert v (Mono k) scope
- | Tycon _ -> scope
- | Tyapp (a, b) -> add_missing_vars (add_missing_vars scope b) a
- | Tyarr (a, b) -> add_missing_vars (add_missing_vars scope b) a
- | Tytup xs -> foldl add_missing_vars scope xs
-
- let tc_program (prog : list decl) =
- let (plan, defs) = dependency_graph prog
- let tc_one (dt_info, val_scope, ty_scope, out) group =
- let defs = [ x | with name <- S.members group, with Some x <- [M.lookup name defs] ]
- match defs with
- | [] -> (dt_info, val_scope, ty_scope, defs)
- | [Foreign (Fimport {var, ftype}) as def] ->
- let ty_scope' = add_missing_vars M.empty ftype
- let t = check_is_type (M.union ty_scope' ty_scope) ftype
- (dt_info, M.insert var (Forall { vars = M.keys ty_scope', body = t } |> Poly) val_scope, ty_scope, def :: out)
- | Cons (Foreign (Fimport {var}), _) ->
- error @@ "Foreign definition " ^ var ^ " is part of a group. How?"
- | Cons (Decl (name, args, body), ds) ->
- let bindings =
- (name, mk_lam args body)
- :: [ (name, mk_lam args body) | with Decl (name, args, body) <- ds ]
- let (bindings, scope') = infer_binding_group dt_info -1 val_scope bindings
- let decs =
- [ Decl (name, unlambda expr) | with (name, expr) <- bindings ]
- (dt_info, M.union (map force scope') val_scope, ty_scope, decs ++ defs)
- | Cons (Data d, ds) ->
- let datas = d :: [ d | with Data d <- ds ]
- let r = infer_data_group_kind ty_scope datas
- let fix_constr (name, rhos : list tc_rho) =
- Constr (name, replicate (length rhos) (Tycon "#"))
- let rec go dt ty (vl : M.t string (scheme tc_rho)) ds = function
- | [] -> (dt, vl, ty, reverse ds ++ out)
- | Cons ((name, kind, constrs, info : list dt_info), rest) ->
- go
- (foldl (fun i {name} -> M.insert name info i) dt info)
- (M.insert name (Mono kind) ty)
- (foldl
- (fun s {name,d_args,c_args,c_ret} ->
- M.insert name (Forall { vars = d_args, body = foldr (curry T_arr) c_ret c_args} |> Poly) s)
- vl info)
- (Data (name, [], fix_constr <$> constrs) :: ds)
- rest
- go dt_info ty_scope val_scope [] r
- let (_, _, _, p) = foldl tc_one (M.empty, M.empty, M.empty, []) plan
- p
|