There is my toy GADT expression:
type _ expr =
| Num : int -> int expr
| Add : int expr * int expr -> int expr
| Sub : int expr * int expr -> int expr
| Mul : int expr * int expr -> int expr
| Div : int expr * int expr -> int expr
| Lt : int expr * int expr -> bool expr
| Gt : int expr * int expr -> bool expr
| And : bool expr * bool expr -> bool expr
| Or : bool expr * bool expr -> bool expr
Evaluation function:
let rec eval : type a. a expr -> a = function
| Num n -> n
| Add (a, b) -> (eval a) + (eval b)
| Sub (a, b) -> (eval a) - (eval b)
| Mul (a, b) -> (eval a) * (eval b)
| Div (a, b) -> (eval a) / (eval b)
| Lt (a, b) -> (eval a) < (eval b)
| Gt (a, b) -> (eval a) > (eval b)
| And (a, b) -> (eval a) && (eval b)
| Or (a, b) -> (eval a) || (eval b)
Creating expression is trivial when we limited to int expr:
let create_expr op a b =
match op with
| '+' -> Add (a, b)
| '-' -> Sub (a, b)
| '*' -> Mul (a, b)
| '/' -> Div (a, b)
| _ -> assert false
The question is how to support both int expr and bool expr in create_expr function.
My try:
type expr' = Int_expr of int expr | Bool_expr of bool expr
let concrete : type a. a expr -> expr' = function
| Num _ as expr -> Int_expr expr
| Add _ as expr -> Int_expr expr
| Sub _ as expr -> Int_expr expr
| Mul _ as expr -> Int_expr expr
| Div _ as expr -> Int_expr expr
| Lt _ as expr -> Bool_expr expr
| Gt _ as expr -> Bool_expr expr
| And _ as expr -> Bool_expr expr
| Or _ as expr -> Bool_expr expr
let create_expr (type a) op (a:a expr) (b:a expr) : a expr =
match op, concrete a, concrete b with
| '+', Int_expr a, Int_expr b -> Add (a, b)
| '-', Int_expr a, Int_expr b -> Sub (a, b)
| '*', Int_expr a, Int_expr b -> Mul (a, b)
| '/', Int_expr a, Int_expr b -> Div (a, b)
| '<', Int_expr a, Int_expr b -> Lt (a, b)
| '>', Int_expr a, Int_expr b -> Gt (a, b)
| '&', Bool_expr a, Bool_expr b -> And (a, b)
| '|', Bool_expr a, Bool_expr b -> Or (a, b)
| _ -> assert false
It still can't return value of generalized type.
Error: This expression has type int expr
but an expression was expected of type a expr
Type int is not compatible with type a
UPDATE
Thanks to #gsg, I was able to implement type safe evaluator. Two tricks are crucial there:
existential wrapper Any
type tagging (TyInt and TyBool) that lets us to pattern match Any type
see
type _ ty =
| TyInt : int ty
| TyBool : bool ty
type any_expr = Any : 'a ty * 'a expr -> any_expr
let create_expr op a b =
match op, a, b with
| '+', Any (TyInt, a), Any (TyInt, b) -> Any (TyInt, Add (a, b))
| '-', Any (TyInt, a), Any (TyInt, b) -> Any (TyInt, Sub (a, b))
| '*', Any (TyInt, a), Any (TyInt, b) -> Any (TyInt, Mul (a, b))
| '/', Any (TyInt, a), Any (TyInt, b) -> Any (TyInt, Div (a, b))
| '<', Any (TyInt, a), Any (TyInt, b) -> Any (TyBool, Lt (a, b))
| '>', Any (TyInt, a), Any (TyInt, b) -> Any (TyBool, Gt (a, b))
| '&', Any (TyBool, a), Any (TyBool, b) -> Any (TyBool, And (a, b))
| '|', Any (TyBool, a), Any (TyBool, b) -> Any (TyBool, Or (a, b))
| _, _, _ -> assert false
let eval_any : any_expr -> [> `Int of int | `Bool of bool] = function
| Any (TyInt, expr) -> `Int (eval expr)
| Any (TyBool, expr) -> `Bool (eval expr)
As you've found, this approach doesn't type check. It also has a more fundamental problem: GADTs can be recursive, in which case it is flatly impossible to enumerate their cases.
Instead you can reify types as a GADT and pass them around. Here's a cut-down example:
type _ expr =
| Num : int -> int expr
| Add : int expr * int expr -> int expr
| Lt : int expr * int expr -> bool expr
| And : bool expr * bool expr -> bool expr
type _ ty =
| TyInt : int ty
| TyBool : bool ty
let bin_op (type a) (type b) op (l : a expr) (r : a expr) (arg_ty : a ty) (ret_ty : b ty) : b expr =
match op, arg_ty, ret_ty with
| '+', TyInt, TyInt -> Add (l, r)
| '<', TyInt, TyBool -> Lt (l, r)
| '&', TyBool, TyBool -> And (l, r)
| _, _, _ -> assert false
At some point you are going to want to have a value that can be 'any expression'. Introducing an existential wrapper allows this. Cheesy example: generating random expression trees:
type any_expr = Any : _ expr -> any_expr
let rec random_int_expr () =
if Random.bool () then Num (Random.int max_int)
else Add (random_int_expr (), random_int_expr ())
let rec random_bool_expr () =
if Random.bool () then Lt (Num (Random.int max_int), Num (Random.int max_int))
else And (random_bool_expr (), random_bool_expr ())
let random_expr () =
if Random.bool () then Any (random_int_expr ())
else Any (random_bool_expr ())
Your stated type for create_expr is char -> 'a expr -> 'a expr -> 'a expr. But the type for the '>' case would be char -> int expr -> int expr -> bool expr. So it seems there are problems with the basic plan.
In essence you want the type of the result to depend on the value of the character. I'm not absolutely positive, but I suspect this isn't possible in OCaml. Seems like it would require a more powerful type system.
Related
I was working on chapter 1 of Modern Compiler Implementation in ML by Andrew Appel and I decided to implement it in OCaml instead of SML. I'm new to OCaml and I came across a very frustrating problem. OCaml seems to think that the below function has the signature int * (int * 'a) -> 'a option.
let rec lookupTable = function
| name, (i, v) :: _ when name = i -> Some v
| name, (_, _) :: rest -> lookupTable (name, rest)
| _, [] -> None
But as far as I can tell, there should be nothing that suggests that the first element in the tuple is an int. This is a problem because when the lookupTable function down the line, the compiler complains that I am not passing it an integer. Perhaps I am missing something incredibly obvious, but it has been pretty mind-boggling. Here is the rest of the program
open Base
type id = string
type binop = Plus | Minus | Times | Div
type stm =
| CompoundStm of stm * stm
| AssignStm of id * exp
| PrintStm of exp list
and exp =
| IdExp of id
| NumExp of int
| OpExp of exp * binop * exp
| EseqExp of stm * exp
(* Returns the maximum number of arguments of any print
statement within any subexpression of a given statement *)
let rec maxargs s =
match s with
| CompoundStm (stm1, stm2) -> Int.max (maxargs stm1) (maxargs stm2)
| AssignStm (_, exp) -> maxargs_exp exp
(* Might be more nested expressions *)
| PrintStm exps -> Int.max (List.length exps) (maxargs_explist exps)
and maxargs_exp e = match e with EseqExp (stm, _) -> maxargs stm | _ -> 0
and maxargs_explist exps =
match exps with
| exp :: rest -> Int.max (maxargs_exp exp) (maxargs_explist rest)
| [] -> 0
type table = (id * int) list
let updateTable name value t : table = (name, value) :: t
let rec lookupTable = function
| name, (i, v) :: _ when name = i -> Some v
| name, (_, _) :: rest -> lookupTable (name, rest)
| _, [] -> None
exception UndefinedVariable of string
let rec interp s =
let t = [] in
interpStm s t
and interpStm s t =
match s with
| CompoundStm (stm1, stm2) -> interpStm stm2 (interpStm stm1 t)
| AssignStm (id, exp) ->
let v, t' = interpExp exp t in
updateTable id v t'
(* Might be more nested expressions *)
| PrintStm exps ->
let interpretAndPrint t e =
let v, t' = interpExp e t in
Stdio.print_endline (Int.to_string v);
t'
in
List.fold_left exps ~init:t ~f:interpretAndPrint
and interpExp e t =
match e with
| IdExp i -> (
match lookupTable (i, t) with
| Some v -> (v, t)
| None -> raise (UndefinedVariable i))
| NumExp i -> (i, t)
| OpExp (exp1, binop, exp2) ->
let exp1_val, t' = interpExp exp1 t in
let exp2_val, _ = interpExp exp2 t' in
let res =
match binop with
| Plus -> exp1_val + exp2_val
| Minus -> exp1_val - exp2_val
| Times -> exp1_val * exp2_val
| Div -> exp1_val / exp2_val
in
(res, t')
| EseqExp (s, e) -> interpExp e (interpStm s t)
Base defines = as int -> int -> bool, so when you have the expression name = i the compiler will infer them as ints.
You can access the polymorphic functions and operators through the Poly module, or use a type-specific operator by locally opening the relevant module, e.g. String.(name = i).
The reason Base does not expose polymorphic operators by default is briefly explained in the documentation's introduction:
The comparison operators exposed by the OCaml standard library are polymorphic:
What they implement is structural comparison of the runtime representation of values. Since these are often error-prone, i.e., they don't correspond to what the user expects, they are not exposed directly by Base.
There's also a performance-argument to be made, because the polymorphic/structural operators need to also inspect what kind of value it is at runtime in order to compare them correctly.
I'm currently doing a project where I have DNA lists of nucleotides like [T;A;C;G;G;C;T;A;G;A;T;T;T;A;C;G;C;T;A;A;T;A;T;C] and I need to convert the nucleotides between the first chain ("START" and "STOP") into their corresponding acids. So for that I need to get the nucleotides 3 by 3 and pass them into this function:
type acid = Ala | Arg | Asn | Asp | Cys
| Glu | Gln | Gly | His | Ile
| Leu | Lys | Phe | Pro | Ser
| Thr | Trp | Tyr | Val | START | STOP
let convert_acid (n1 : nucleotide) (n2 : nucleotide) (n3 : nucleotide) : acid =
begin match (n1, n2, n3) with
| (A, A, A) -> Phe | (A, A, G) -> Phe | (A, A, T) -> Leu | (A, A, C) -> Leu
| (G, A, A) -> Leu | (G, A, G) -> Leu | (G, A, T) -> Leu | (G, A, C) -> Leu
| (T, A, A) -> Ile | (T, A, G) -> Ile | (T, A, T) -> Ile | (T, A, C) -> START
| (C, A, A) -> Val | (C, A, G) -> Val | (C, A, T) -> Val | (C, A, C) -> Val
| (A, G, A) -> Ser | (A, G, G) -> Ser | (A, G, T) -> Ser | (A, G, C) -> Ser
| (G, G, A) -> Pro | (G, G, G) -> Pro | (G, G, T) -> Pro | (G, G, C) -> Pro
| (T, G, A) -> Thr | (T, G, G) -> Thr | (T, G, T) -> Thr | (T, G, C) -> Thr
| (C, G, A) -> Ala | (C, G, G) -> Ala | (C, G, T) -> Ala | (C, G, C) -> Ala
| (A, T, A) -> Tyr | (A, T, G) -> Tyr | (A, T, T) -> STOP | (A, T, C) -> STOP
| (G, T, A) -> His | (G, T, G) -> His | (G, T, T) -> Gln | (G, T, C) -> Gln
| (T, T, A) -> Asn | (T, T, G) -> Asn | (T, T, T) -> Lys | (T, T, C) -> Lys
| (C, T, A) -> Asp | (C, T, G) -> Asp | (C, T, T) -> Glu | (C, T, C) -> Glu
| (A, C, A) -> Cys | (A, C, G) -> Cys | (A, C, T) -> STOP | (A, C, C) -> Trp
| (G, C, A) -> Arg | (G, C, G) -> Arg | (G, C, T) -> Arg | (G, C, C) -> Arg
| (T, C, A) -> Ser | (T, C, G) -> Ser | (T, C, T) -> Arg | (T, C, C) -> Arg
| (C, C, A) -> Gly | (C, C, G) -> Gly | (C, C, T) -> Gly | (C, C, C) -> Gly
end
So my idea is to get the first 3 nucleotide of the list, convert them by passing them into the function, and concatenating the returned list into a new list, but I don't know how to do that. Here is what I have done so far in pseudocode:
let rec dna_to_chain (x : dna) : acid list =
match with x
| hd::tl -> convert_acid hd
| _ -> do nothing
I would like to get the first 3 elements of the list x, pass them into the convert_acid function, then calling dna_to_chain with the rest of the list till I reach the "STOP" acid.
After that I need to do an another function to convert each dna into chains (each sequences of nucleotides between a START and STOP), and put them into a list of list of acids with a similar function without using recursion (I guess that I need to use the recursive function that i need to do first).
Anyone know how I could finish my code and make it works? Thanks!
EDIT:
I now have the following function
let rec dna_to_chain (x : dna) : acid list =
match with x
| n1::n2::n3::tl -> (convert_acid n1 n2 n3) :: dna_to_chain tl
| [] -> [] ;;
dna_to_chain [T;A;C;G;G;C;T;A;G;A;T;T ; T;A;C;G;C;T;A;A;T;A;T;C] returns [START; Pro; Ile; STOP ; START;Arg;Leu;STOP] but how can I get the first acids between the first START and STOP? I'm thinking of making an another function and doing pattern matching again but how do I know that the START that I'm matching is the first one of the chain for example?
First don't put the START in your result list in your dna_to_chain function. You can ignore them because they are useless for the rest of your problem.
Then you just have to write a "split" function, which is a good exercice.
Split take a l ('a list) and v ('a) as argument and return the sublists between each occurence of v in the list. For exemple :
split [2;3;3;1;2;3;4;5;1;2;1;3;1;8;9] 1;;
- : int list list = [[2; 3; 3]; [2; 3; 4; 5]; [2]; [3]; [8; 9]]
Once you did that you just have to use List.map with your dna to chain function on the result of
split (dna_to_chain (*your dna*)) STOP
Disclaimer : I don't know if that's the good way to answer on SO so if I should edit my old answer instead of writing a new one just tell me.
So here are 2 versions of the split code I talked about in my previous answer. I hope it'll help you to figure how to achieve your modified split function. The first one use 2 recursive function and the second one is tail recursive
let rec split v = function
| [] -> []
| t -> let a, b = goNext v t in
if a <> [] then a :: split v b else split v b
and goNext v = function
| x :: xs -> if x = v then [], xs else
let a, b = goNext v xs in x :: a, b
| [] -> ([], []);;
let split2 l v =
let rec aux acc buff = function
| x :: xs -> if x = v
then
aux [] (if acc <> [] then List.rev acc :: buff else buff) xs
else
aux (x :: acc) buff xs
| [] -> List.rev ## if acc <> [] then List.rev acc :: buff else buff
in aux [] [] l;;
Both function ignore empty list between 2 occurrences of v
Hopefully this is useful as an answer, because a comment is a bit limited to detail this.
let between start_tok stop_tok lst =
let (_, _, result) = List.fold_left
(fun (start_seen, acc, overall_acc) x ->
if start_seen && x = start_tok then
(true, [], overall_acc)
else if start_seen && x = stop_tok then
(false, [], overall_acc # [acc])
else if start_seen then
(true, acc # [x], overall_acc)
else if x = start_tok then
(true, [], overall_acc)
else
(false, acc, overall_acc))
(false, [], [])
lst
in
result
We fold over a list of type 'a list having provided start and stop tokens of type 'a. Our initial value to the fold is a tuple containing whether the start token has been seen, an accumulator, and an overall accumulator. As it goes, if it's between a start and stop token, it adds to the accumulator. When it hits a stop, that accumulator gets added to the overall accumulator. At the end we use pattern matching to just access the overall accumulated list.
There's nothing her to handle errors where either the start or stop are not encountered. I'll leave that as a further exercise for you.
I find the confines of a fold are a great way to look at what information actually needs to be communicated across iterations over a list.
Sorry about the "what am I missing here" style of question here, but I'm just missing something here.
I was trying to understand how GADTs work in OCaml, I define the following (in utop):
type value =
| Bool : bool -> value
| Int : int -> value
;;
type _ value =
| Bool : bool -> bool value
| Int : int -> int value
;;
type _ expr =
| Value : 'a value -> 'a expr
| If : bool expr * 'a expr * 'a expr -> 'a expr
| Lt : 'a expr * 'a expr -> bool expr
| Eq : 'a expr * 'a expr -> bool expr
| Gt : 'a expr * 'a expr -> bool expr
;;
I defined an eval function:
let rec eval : type a. a expr -> a = function
| Value (Int i) -> i
| Value (Bool b) -> b
| Lt (a, b) -> (eval a) < (eval b)
| Gt (a, b) -> (eval a) > (eval b)
| Eq (a, b) -> (eval a) = (eval b)
| If (c, a, b) -> if eval c then (eval a) else (eval b)
;;
but got an error:
Line 4, characters 15-23:
Error: This expression has type $Lt_'a but an expression was expected of type
int
What exactly does this mean?
Just to test further, I modified the expression GADT to be:
type _ expr =
| Value : 'a value -> 'a expr
| If : bool expr * 'a expr * 'a expr -> 'a expr
| Lt : int expr * int expr -> bool expr
| Eq : 'a expr * 'a expr -> bool expr
| Gt : int expr * int expr -> bool expr
;;
and then I see
Line 6, characters 15-23:
Error: This expression has type $Eq_'a but an expression was expected of type
int
When I finally modify it to be
type _ expr =
| Value : 'a value -> 'a expr
| If : bool expr * 'a expr * 'a expr -> 'a expr
| Lt : int expr * int expr -> bool expr
| Eq : int expr * int expr -> bool expr
| Gt : int expr * int expr -> bool expr
;;
it works fine.
Update (more context):
Ocaml version: 4.08.1
Libraries opened during this session: Base
Update (solution):
it turned out to be (as mentioned in the first line of the selected answer) because I had previously, within utop run open Base ;;
In a fresh session I'm able to enter the types initially mentioned and eval is happy with that.
The direct cause of the error is that you are using a library (maybe Base or Core?) that shadows the polymorphic comparison operators (<,<=,=,>=,>) and replace them with integer comparison operators.
Concerning the error message, when you pattern match a GADT constructor with existential types,
| Lt (a, b) -> (eval a) < (eval b)
the typechecker introduces new types to represent the existential types.
Here, in the (original) definition of Lt,
| Lt : 'a expr * 'a expr -> bool expr
there is one existentially quantified type variable: 'a.
When pattern matching on Lt, we need to replace this type variable with
a new type. Moreover, it is quite useful in error message to try to pick
a meaningful name for this type. To do so, the typechecker constructs a
new type name piece by piece as $ + Lt + 'a:
$: to mark an existential type
Lt: to indicate that it was introduced by the constructor Lt
a: to remember that the existential type variable was named 'a in the definition of the constructor
In other words, in the pattern match above, we have something akin to
| Lt ( (a: $Lt_'a eval), (b: $Lt_'a eval)) -> (eval a) < (eval b)
And when typing:
(eval a) < (eval b)
the typechecker compare the type of <: int -> int with the type of eval a: $Lt_'a and outputs your original error message:
Line 4, characters 15-23:
Error: This expression has type $Lt_'a but an expression was expected of type
int
I have code below, but I don't get the correct result I want
type expr
= Val of value
| App of expr * expr
| MultiApp of expr * expr list
and value
= Int of int
| Bool of bool
let rec t(e:expr) : expr = match e with
| Val _ -> e
| App(f,a) -> App(t f, t a)
| MultiApp(f,[]) -> raise (Failure "no")
| MultiApp(f,lst) -> match lst with
|[]-> raise (Failure "no")
|x::xs -> let rest = MultiApp(f,xs) in
App(f,x)
let e1 = Val(Int 1)
let e2 = Val(Int 3)
let e3 = Val(Bool true)
let e4 = Val(Bool false)
For example,
MultiApp(fe,[e1;e2;e3])
after apply fun t it would get something like
App(App(App(t fe, t e1), t e2) t e3)
HI I have the following define type and try to eval the function as example:
let evn =[("z1",Int 0);("x",Int 1);("y",Int 2);("z",Int 3);("z1",Int 4)];;
val evn : (string * Nano.value) list = [("z1", Int 0); ("x", Int 1); ("y", Int 2); ("z", Int 3); ("z1", Int 4)]
# let e1 =Bin(Bin(Var "x",Plus,Var "y"), Minus, Bin(Var "z",Plus,Var "z1"));;
val e1 : Nano.expr = Bin (Bin (Var "x", Plus, Var "y"), Minus, Bin (Var "z", Plus, Var "z1"))
# eval (evn,e1);;
- : Nano.value = Int 0
# eval (evn,Var "p");;
Exception: Nano.MLFailure "Variable not bound: p".
somehow i got a erros in the second bin match in the eval function saying :
This pattern matches values of type expr
but a pattern was expected which matches values of type
int option * int option
type binop = Plus | Minus | Mul | Div
type expr = Const of int
| Var of string
| Bin of expr * binop * expr
type value = Int of int
type env = (string * value) list
here is the programs:
exception MLFailure of string
type binop =
Plus
| Minus
| Mul
| Div
| Eq
| Ne
| Lt
| Le
| And
| Or
| Cons
type expr =
Const of int
| True
| False
| NilExpr
| Var of string
| Bin of expr * binop * expr
| If of expr * expr * expr
| Let of string * expr * expr
| App of expr * expr
| Fun of string * expr
| Letrec of string * expr * expr
type value =
Int of int
| Bool of bool
| Closure of env * string option * string * expr
| Nil
| Pair of value * value
and env = (string * value) list
let binopToString op =
match op with
Plus -> "+"
| Minus -> "-"
| Mul -> "*"
| Div -> "/"
| Eq -> "="
| Ne -> "!="
| Lt -> "<"
| Le -> "<="
| And -> "&&"
| Or -> "||"
| Cons -> "::"
let rec valueToString v =
match v with
Int i ->
Printf.sprintf "%d" i
| Bool b ->
Printf.sprintf "%b" b
| Closure (evn,fo,x,e) ->
let fs = match fo with None -> "Anon" | Some fs -> fs in
Printf.sprintf "{%s,%s,%s,%s}" (envToString evn) fs x (exprToString e)
| Pair (v1,v2) ->
Printf.sprintf "(%s::%s)" (valueToString v1) (valueToString v2)
| Nil ->
"[]"
and envToString evn =
let xs = List.map (fun (x,v) -> Printf.sprintf "%s:%s" x (valueToString v)) evn in
"["^(String.concat ";" xs)^"]"
and exprToString e =
match e with
Const i ->
Printf.sprintf "%d" i
| True ->
"true"
| False ->
"false"
| Var x ->
x
| Bin (e1,op,e2) ->
Printf.sprintf "%s %s %s"
(exprToString e1) (binopToString op) (exprToString e2)
| If (e1,e2,e3) ->
Printf.sprintf "if %s then %s else %s"
(exprToString e1) (exprToString e2) (exprToString e3)
| Let (x,e1,e2) ->
Printf.sprintf "let %s = %s in \n %s"
x (exprToString e1) (exprToString e2)
| App (e1,e2) ->
Printf.sprintf "(%s %s)" (exprToString e1) (exprToString e2)
| Fun (x,e) ->
Printf.sprintf "fun %s -> %s" x (exprToString e)
| Letrec (x,e1,e2) ->
Printf.sprintf "let rec %s = %s in \n %s"
x (exprToString e1) (exprToString e2)
let rec fold f base args =
match args with [] -> base
| h::t -> fold f (f(base,h)) t
let listAssoc (k,l) =
fold (fun (r,(t,v)) -> if r = None && k=t then Some v else r) None l
let lookup (x,evn) =
let n = listAssoc (x,evn) in
match n with
| None -> raise (MLFailure x)
| Some x -> x
let rec eval (evn,e) = match e with
| Const i -> Some i
| Var v -> lookup (v,evn)
| Bin(e1, Plus, e2) -> match (eval (evn,e1), eval (evn,e2)) with
| (Some a, Some b) -> Some (a + b)
| (Some c, None) -> raise (MLFailure c)
| (None, Some a) -> raise (MLFailure a)
(here is the where the erro causing *)
| Bin(e1, Div, e2) -> match (eval (evn,e1), eval (evn,e2)) with
| (Some a, Some b) -> Some (a / b)
| (Some c, None) -> raise (MLFailure c)
| (None, Some a) -> raise (MLFailure a)
| Bin(e1, Minus, e2) -> match (eval (evn,e1), eval (evn,e2)) with
| (Some a, Some b) -> Some (a - b)
| (Some c, None) -> raise (MLFailure c)
| (None, Some a) -> raise (MLFailure a)
| Bin(e1, Mul, e2) -> match (eval (evn,e1), eval (evn,e2)) with
| (Some a, Some b) -> Some (a * b)
| (Some c, None) -> raise (MLFailure c)
| (None, Some a) -> raise (MLFailure a)
Be careful, with nested match-with do not forget to add begin-end statements. That's why you have this error.
let rec eval (evn,e) = match e with
| Const i -> Some i
| Var v -> lookup (v,evn)
| Bin(e1, Plus, e2) ->
begin match (eval (evn,e1), eval (evn,e2)) with
| (Some a, Some b) -> Some (a + b)
| (Some c, None) -> raise (MLFailure c)
| (None, Some a) -> raise (MLFailure a)
end
| Bin(e1, Div, e2) ->
begin match (eval (evn,e1), eval (evn,e2)) with
| (Some a, Some b) -> Some (a / b)
| (Some c, None) -> raise (MLFailure c)
| (None, Some a) -> raise (MLFailure a)
end
| Bin(e1, Minus, e2) ->
begin match (eval (evn,e1), eval (evn,e2)) with
| (Some a, Some b) -> Some (a - b)
| (Some c, None) -> raise (MLFailure c)
| (None, Some a) -> raise (MLFailure a)
end
| Bin(e1, Mul, e2) ->
begin match (eval (evn,e1), eval (evn,e2)) with
| (Some a, Some b) -> Some (a * b)
| (Some c, None) -> raise (MLFailure c)
| (None, Some a) -> raise (MLFailure a)
end
If you don't do this, it's like you wrote as follow:
let rec eval (evn,e) = match e with
| Const i -> Some i
| Var v -> lookup (v,evn)
| Bin(e1, Plus, e2) -> match (eval (evn,e1), eval (evn,e2)) with
| (Some a, Some b) -> Some (a + b)
| (Some c, None) -> raise (MLFailure c)
| (None, Some a) -> raise (MLFailure a)
| Bin(e1, Div, e2) -> match (eval (evn,e1), eval (evn,e2)) with
| (Some a, Some b) -> Some (a / b)
| (Some c, None) -> raise (MLFailure c)
| (None, Some a) -> raise (MLFailure a)
| Bin(e1, Minus, e2) -> (* ... *)