I have just learned something about GADT in OCaml through Real World OCaml, and want to try to transfer the first little language there into a interpreter, thus using menhir.
The ast definition is totally the same as the example.Here is it:
type _ value =
| Int : int -> int value
| Bool : bool -> bool value
type _ expr =
| Value: 'a value -> 'a expr
| Eq : int expr * int expr -> bool expr
| Plus : int expr * int expr -> int expr
| If : bool expr * 'a expr * 'a expr -> 'a expr
let eval_value : type a. a value -> a = function
| Int x -> x
| Bool x -> x ;;
let rec eval: type a. a expr -> a = function
| Value v -> eval_value v
| If (c, t, e) -> if eval c then eval t else eval e
| Eq (x, y) -> eval x = eval y
| Plus (x, y) -> eval x + eval y;
And here is my lexer and parser specification
{
open Parser
exception Error of string
}
rule token = parse
| [' ' '\t'] {token lexbuf}
| '\n' {Lexing.new_line lexbuf; token lexbuf}
| "if" {IF}
| "then" {THEN}
| "else" {ELSE}
| '=' {EQ}
| '+' {PLUS}
| "true" {BOOL (true)}
| "FALSE" {BOOL (false)}
| ['0'-'9']+ as i {NUM (int_of_string i)}
| '-'['0'-'9']+ as i { NUM (int_of_string i) }
| eof {EOF}
| _ { raise (Error (Printf.sprintf "At offset %d: unexpected character.\n" (Lexing.lexeme_start lexbuf))) }
%{
open Ast
%}
%token <int> NUM
%token <bool> BOOL
%token PLUS
%token IF
%token THEN
%token ELSE
%token EQ
%token EOF
%left PLUS
%start <'a Ast.expr> expr_toplevel
%%
expr_toplevel:
| e = expr EOF {e}
expr:
| x = expr PLUS y = expr {Plus (x, y)}
| IF c = expr THEN x = expr ELSE y = expr {If (c, x, y)}
| x = expr EQ y = expr {EQ (x, y)}
| b = BOOL {Value (Bool b)}
| n = NUM {Value (Int n)}
That's all, and the code above failed to build, the error it got is:
File "bin/parser.mly", line 27, characters 18-26:
Error: This expression has type bool value
but an expression was expected of type int value
Type bool is not compatible with type int
which is referring to
| b = BOOL {Value (Bool b)}
This error is totally the same as the one we get in the first code block in Chapter GADT, Locally Abstract Types, and Polymorphic Recursion
in Real World OCaml, where the author try to treat GADT as ordinary variance. Is this the same problem here? If yes, how could I fix this to make it work.
There is nothing much about GADT in menhir manual. It is mentioned in a flag named --inspection. I have tried that and nothing changes.
The root issue is that you are trying to write a function of type
val parse: (x:string) -> f(x) Ast.expr
where the type of the parsed ast depends on the value of the input. This is only possible in dependently typed language, and OCaml is not dependently typed.
One solution to make it work is to make the parser return a type that represents any Ast.expr
, which can be defined with an existential quantification:
type any_expr = Any: 'a Ast.expr -> any_expr [@@unboxed]
Then the expr
parsing function will have the type:
val parse: string -> any_expr
and it is still possible to have subparsers that returns a specific type of expr
.