lang-zoo/bidirectional/bidirectional.ml

186 lines
5.8 KiB
OCaml

module StringSet = Set.Make (String)
type name = string
type type' =
(* 1 *)
| Unit
(* x *)
| Variable of name
(* ^x *)
| Existential of name
(* forall a. A *)
| ForAll of name * type'
(* A -> B *)
| Function of type' * type'
let rec free_variables = function
| Unit -> StringSet.empty
| Variable x | Existential x -> StringSet.singleton x
| ForAll (alpha, a) -> StringSet.remove alpha (free_variables a)
| Function (a, b) -> StringSet.inter (free_variables a) (free_variables b)
let occurs_check name type' = free_variables type' |> StringSet.mem name |> not
let rec is_monotype = function
| Unit | Variable _ | Existential _ -> true
| ForAll _ -> false
| Function (a, b) -> is_monotype a && is_monotype b
let rec replace name with_type in_type =
match in_type with
| (Variable n | Existential n) when n = name -> with_type
| Variable _ | Existential _ | Unit -> in_type
| ForAll (alpha, _) when name = alpha -> in_type
| ForAll (alpha, a) -> ForAll (alpha, replace name with_type a)
| Function (a, b) ->
Function (replace name with_type a, replace name with_type b)
type term =
(* () *)
| Unit
(* x *)
| Variable of name
(* \x. e *)
| Lambda of name * term
(* f x *)
| Application of term * term
(* x : T *)
| Annotation of term * type'
type judgment =
(* universally quantified *)
| ForAll of name
| Assume of name * type'
(* existentially quantified *)
| Exists of name
| Solved of name * type'
(* marker *)
| Marker of name
type context = judgment list
let existentials =
List.fold_left
(fun vars j ->
match j with
| Exists n | Solved (n, _) ->
StringSet.singleton n |> StringSet.union vars
| _ -> vars)
StringSet.empty
let in_existentials name gamma = existentials gamma |> StringSet.mem name
exception TypeError of string
let lookup_name context name =
let find_name = function
| (ForAll n | Assume (n, _) | Exists n | Solved (n, _)) when n = name ->
true
| _ -> false
in
List.find find_name context
let rec drop_marker marker context =
match context with
| [] -> raise (TypeError "tried to drop missing marker")
| top :: rest when top = marker -> rest
| _ :: rest -> drop_marker marker rest
let solve name solution context =
let solve' = function
| Exists n when n = name -> Solved (n, solution)
| j -> j
in
List.map solve' context
let rec substitute gamma (a : type') =
match a with
| Unit | Variable _ -> a
| Existential alpha -> (
match lookup_name gamma alpha with
| Exists _ -> a
| Solved (_, tau) -> substitute gamma tau
| _ -> raise (TypeError "unbound existential"))
| Function (a, b) -> Function (substitute gamma a, substitute gamma b)
| ForAll (alpha, a) -> ForAll (alpha, substitute gamma a)
let mk_fresh_generator prefix =
let counter = ref 0 in
fun base ->
let output = prefix ^ base ^ string_of_int !counter in
counter := !counter + 1;
output
let fresh_variable = mk_fresh_generator "$"
let fresh_type_variable = mk_fresh_generator "'"
let rec instantiate_left alpha a gamma =
match a with
| _ when is_monotype a -> solve alpha a gamma
| Existential beta -> solve beta (Existential alpha) gamma
| Function (a1, a2) ->
let alpha_1 = fresh_type_variable alpha in
let alpha_2 = fresh_type_variable alpha in
let theta =
instantiate_right a1 alpha_1
(Solved (alpha, Function (Variable alpha_1, Variable alpha_2))
:: Exists alpha_1 :: Exists alpha_2 :: gamma)
in
instantiate_left alpha_2 (substitute theta a2) theta
| ForAll (beta, b) ->
let beta' = fresh_type_variable beta in
instantiate_left alpha b (ForAll beta' :: gamma)
|> drop_marker (ForAll beta')
| _ -> raise (TypeError "bad left instantiation")
and instantiate_right a alpha gamma =
match a with
| _ when is_monotype a -> solve alpha a gamma
| Existential beta -> solve beta (Existential alpha) gamma
| Function (a1, a2) ->
let alpha_1 = fresh_type_variable alpha in
let alpha_2 = fresh_type_variable alpha in
let theta =
instantiate_left alpha_1 a1
(Solved (alpha, Function (Variable alpha_1, Variable alpha_2))
:: Exists alpha_1 :: Exists alpha_2 :: gamma)
in
instantiate_right (substitute theta a2) alpha_2 theta
| ForAll (beta, b) ->
let beta' = fresh_type_variable beta in
instantiate_right b alpha (ForAll beta' :: gamma)
|> drop_marker (ForAll beta')
| _ -> raise (TypeError "bad right instantiation")
let rec subtype (a : type') (b : type') gamma =
match (a, b) with
| Variable a, Variable b when a = b -> gamma
| Unit, Unit -> gamma
| Existential a, Existential b when a = b && in_existentials a gamma -> gamma
| Function (a1, a2), Function (b1, b2) ->
let theta = subtype b1 a1 gamma in
subtype (substitute theta a2) (substitute theta b2) theta
| ForAll (alpha, a), _ ->
let alpha' = fresh_type_variable alpha in
let gamma = Exists alpha' :: Marker alpha' :: gamma in
let a' = replace alpha (Existential alpha') a in
subtype a' b gamma |> drop_marker (Marker alpha')
| _, ForAll (beta, b) ->
let beta' = fresh_type_variable beta in
let b' = replace beta (Variable beta') b in
subtype a b' (ForAll beta' :: gamma) |> drop_marker (ForAll beta')
| Existential alpha, _ when occurs_check alpha b ->
instantiate_left alpha b gamma
| _, Existential beta when occurs_check beta a ->
instantiate_right a beta gamma
| _ -> raise (TypeError "invalid subtype relation")
let rec check (e : term) (a : type') gamma =
match (e, a) with _ -> raise (TypeError "bad check")
and synth (e : term) gamma = match e with _ -> raise (TypeError "bad synth")
and apply (a : type') (e : term) gamma =
match (a, e) with _ -> raise (TypeError "bad apply")