Skip to content

Commit e438c11

Browse files
committed
Add bidi_local
bidi_local attempts to implement "Local Type Inference" by Pierce and Turner.
1 parent d379e6e commit e438c11

27 files changed

+2933
-0
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@
2626
Type inference and elaborator are implemented but the environment construction
2727
doesn't check for overlapping instances yet.
2828

29+
- `bidi_local` attempts to implement ["Local Type Inference"][bidi_local] by
30+
Piece and Turner.
31+
32+
This is a bidirectional type checking mechanism where we alternate between
33+
checking mode (where we have type annotations available) and synthesis mode.
34+
35+
Thus this requires type annotations for let-functions declarations.
36+
37+
The type system supports subtyping (with `null`). The way it works is it
38+
collects lower/upper bound constraints for type variables and then synthesises
39+
the most general type for type variables at polymorphic abstraction
40+
elimination.
41+
42+
2943
# Development
3044

3145
```
@@ -48,3 +62,4 @@ make test
4862
[inferno]: https://gitlab.inria.fr/fpottier/inferno/
4963
[THIH]: https://web.cecs.pdx.edu/~mpj/thih/thih.pdf
5064
[tomprimozic/type-systems]: https://github.com/tomprimozic/type-systems
65+
[bidi_local]: https://www.cis.upenn.edu/~bcpierce/papers/lti-toplas.pdf

bidi_local/bidi_local.ml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
module Expr = struct
2+
include Syntax.Expr
3+
4+
include Nice_parser.Make (struct
5+
type token = Parser.token
6+
7+
type result = Syntax.expr
8+
9+
let parse = Parser.expr_eof
10+
11+
let next_token = Lexer.token
12+
13+
exception ParseError = Parser.Error
14+
15+
exception LexError = Lexer.Error
16+
end)
17+
end
18+
19+
module Ty = struct
20+
include Syntax.Ty
21+
22+
include Nice_parser.Make (struct
23+
type token = Parser.token
24+
25+
type result = Syntax.ty
26+
27+
let parse = Parser.ty_eof
28+
29+
let next_token = Lexer.token
30+
31+
exception ParseError = Parser.Error
32+
33+
exception LexError = Lexer.Error
34+
end)
35+
end
36+
37+
module Ty_sch = struct
38+
include Syntax.Ty_sch
39+
40+
include Nice_parser.Make (struct
41+
type token = Parser.token
42+
43+
type result = Syntax.ty_sch
44+
45+
let parse = Parser.ty_sch_eof
46+
47+
let next_token = Lexer.token
48+
49+
exception ParseError = Parser.Error
50+
51+
exception LexError = Lexer.Error
52+
end)
53+
end
54+
55+
module Type_error = Type_error
56+
module Var = Var
57+
58+
module Env = struct
59+
include Infer.Env
60+
61+
let assume_val name ty env =
62+
add_val ~kind:Val_top env name (Ty_sch.parse_string ty)
63+
end
64+
65+
let infer = Infer.infer

bidi_local/bin/dune

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(executable
2+
(public_name bidi_local)
3+
(name main)
4+
(libraries bidi_local base stdio))

bidi_local/bin/main.ml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
open Base
2+
open Bidi_local
3+
4+
let () =
5+
let env =
6+
Env.empty
7+
|> Env.assume_val "id" "a . a -> a"
8+
|> Env.assume_val "null" "a . a?"
9+
|> Env.assume_val "one" "int"
10+
|> Env.assume_val "nil" "a . list[a]"
11+
|> Env.assume_val "cons" "a . (a, list[a]) -> list[a]"
12+
|> Env.assume_val "map" "a, b . (a -> b, list[a]) -> list[b]"
13+
|> Env.assume_val "choose" "a . (a, a) -> a"
14+
|> Env.assume_val "choose3" "a . (a, a, a) -> a"
15+
|> Env.assume_val "choose4" "a . (a, a, a, a) -> a"
16+
|> Env.assume_val "hello" "string"
17+
|> Env.assume_val "pair" "a, b . (a, b) -> pair[a, b]"
18+
|> Env.assume_val "plus" "(int, int) -> int"
19+
|> Env.assume_val "true" "bool"
20+
|> Env.assume_val "ifnull" "a . (a?, a) -> a"
21+
|> Env.assume_val "eq" "a . (a, a) -> bool"
22+
in
23+
let e = Expr.parse_chan Stdio.stdin in
24+
match infer ~env e with
25+
| Ok e -> Caml.Format.printf "%s@." (Expr.show e)
26+
| Error err -> Caml.Format.printf "ERROR: %s@." (Type_error.show err)

bidi_local/debug.ml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
(**
2+
3+
Debug flags which influence the amount of logging information and pretty
4+
printing.
5+
6+
Flags are controlled through DEBUG environment variables, use it like:
7+
8+
$ DEBUG=sgi dune exec -- COMMAND
9+
10+
The above invocation will toggle glags [s], [g] and [i] to be "on".
11+
12+
*)
13+
14+
open! Base
15+
16+
let flags = Caml.Sys.getenv_opt "DEBUG" |> Option.value ~default:""
17+
18+
let log_solve = String.mem flags 's'
19+
20+
let log_generalize = String.mem flags 'g'
21+
22+
let log_instantiate = String.mem flags 'i'
23+
24+
let log_check = String.mem flags 'c'
25+
26+
let log_levels = String.mem flags 'l'

bidi_local/dune

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
(library
2+
(name bidi_local)
3+
(preprocess (pps ppx_sexp_conv))
4+
(libraries base pprint nice_parser)
5+
)
6+
7+
(ocamllex lexer)
8+
9+
(menhir
10+
(modules parser)
11+
(flags --explain --dump))

bidi_local/import.ml

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
include Base
2+
3+
module Monad = struct
4+
include Base.Monad
5+
6+
(** A signature for modules implementing monadic let-syntax. *)
7+
module type MONAD_SYNTAX = sig
8+
type 'a t
9+
10+
val return : 'a -> 'a t
11+
12+
val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t
13+
14+
val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t
15+
end
16+
17+
(** Make a monadic syntax out of the monad. *)
18+
module Make_monad_syntax (P : Base.Monad.S) :
19+
MONAD_SYNTAX with type 'a t := 'a P.t = struct
20+
let return = P.return
21+
22+
let ( let* ) v f = P.bind v ~f
23+
24+
let ( let+ ) v f = P.map v ~f
25+
end
26+
27+
module type S = sig
28+
include Base.Monad.S
29+
30+
module Monad_syntax : MONAD_SYNTAX with type 'a t := 'a t
31+
end
32+
33+
module Make (P : Basic) : S with type 'a t := 'a P.t = struct
34+
module Self = Base.Monad.Make (P)
35+
include Self
36+
37+
module Monad_syntax = Make_monad_syntax (struct
38+
type 'a t = 'a P.t
39+
40+
include Self
41+
end)
42+
end
43+
end
44+
45+
module MakeId () = struct
46+
let c = ref 0
47+
48+
let fresh () =
49+
Int.incr c;
50+
!c
51+
52+
let reset () = c := 0
53+
end
54+
55+
module type SHOWABLE = sig
56+
type t
57+
58+
val show : t -> string
59+
60+
val print : ?label:string -> t -> unit
61+
end
62+
63+
module Showable (S : sig
64+
type t
65+
66+
val layout : t -> PPrint.document
67+
end) : SHOWABLE with type t = S.t = struct
68+
type t = S.t
69+
70+
let show v =
71+
let width = 60 in
72+
let buf = Buffer.create 100 in
73+
PPrint.ToBuffer.pretty 1. width buf (S.layout v);
74+
Buffer.contents buf
75+
76+
let print ?label v =
77+
match label with
78+
| Some label -> Caml.print_endline (label ^ ": " ^ show v)
79+
| None -> Caml.print_endline (show v)
80+
end
81+
82+
module type DUMPABLE = sig
83+
type t
84+
85+
val dump : ?label:string -> t -> unit
86+
87+
val sdump : ?label:string -> t -> string
88+
end
89+
90+
module Dumpable (S : sig
91+
type t
92+
93+
val sexp_of_t : t -> Sexp.t
94+
end) : DUMPABLE with type t = S.t = struct
95+
type t = S.t
96+
97+
let dump ?label v =
98+
let s = S.sexp_of_t v in
99+
match label with
100+
| None -> Caml.Format.printf "%a@." Sexp.pp_hum s
101+
| Some label -> Caml.Format.printf "%s %a@." label Sexp.pp_hum s
102+
103+
let sdump ?label v =
104+
let s = S.sexp_of_t v in
105+
match label with
106+
| None -> Caml.Format.asprintf "%a@." Sexp.pp_hum s
107+
| Some label -> Caml.Format.asprintf "%s %a@." label Sexp.pp_hum s
108+
end

0 commit comments

Comments
 (0)