简体   繁体   中英

Strange Ocaml type error

I'm writing an ocaml program to process basic arithmetic commands and symbolic math commands. However, the code is currently giving me a strange type error. I feel like this might arise because there are two different variants that use the binop type, but I'm not sure.

open Core.Std

type binop =
    |Add
    |Subtract
    |Multiply
    |Divide

let apply_binop_int a b = function
    |Add -> a + b
    |Subtract -> a - b
    |Multiply -> a * b
    |Divide -> a / b

let rec apply_binop a b op  =
    match (a,b) with
    |ExprTerm (Const a), ExprTerm (Const b) -> apply_binop_int a b op
    |BinopExpr (op1,a1,b1),_ -> apply_binop (apply_binop a1 b1 op1) b op
    |_,BinopExpr (op1,a1,b1) -> apply_binop a (apply_binop a1 b1 op1) op

let precedence = function
    |Add |Subtract -> 0
    |Multiply |Divide -> 1

type term  =
    |Const of int
    |Var of string

type token =
    |Term of term
    |Operator of binop

type expr =
    |ExprTerm of term
    |BinopExpr of binop * expr * expr

let operator_of_string = function
    |"+" -> Add
    |"-" -> Subtract
    |"*" -> Multiply
    |"/" -> Divide
    |_   -> failwith "Unidentified operator"

let token_of_string s =
    try Term (Const (int_of_string s))with
    |_ -> Operator (operator_of_string s)

let tokens s =
    String.split ~on:' ' s
    |> List.map ~f:token_of_string

let process_operator ops exprs =
    match (ops,exprs) with
    |op::op_tl,b::a::expr_tl -> op_tl,(BinopExpr (op,a,b))::expr_tl
    |_,_ -> failwith "Malformed expression"

let rec pop_stack (ops,exprs) =
    match (ops,exprs) with
    |_::_, _::_::_ -> pop_stack (process_operator ops exprs)
    |_,[x] -> x
    |_,_ -> failwith "Malformed expression"

let build_expr ts =
    let rec aux ops exprs toks =
        match (toks,ops) with
        |Term t::tl,_ -> aux ops ((ExprTerm t)::exprs) tl
        |Operator op2::tl,op::_ when precedence op >= precedence op2 ->
                            let ops,exprs = process_operator ops exprs in
                            aux ops exprs toks
        |Operator op::tl,_ -> aux (op::ops) exprs tl
        |[],_ -> pop_stack (ops,exprs) in
    aux [] [] ts

let expr s = build_expr (tokens s)

let rec eval = function
    |BinopExpr (op,a,b) ->
                        apply_binop (eval a) (eval b) op
    |ExprTerm t -> t

The error that I'm getting:

utop # #use "calc.ml";;
type binop = Add | Subtract | Multiply | Divide                                         
val apply_binop_int : int -> int -> binop -> int = <fun>                               
File "calc.ml", line 18, characters 63-66:
Error: This expression has type binop/1405061 but an expression was expected of type             
binop/1740597

OCaml convention is to always define the types, then define the functions that operate on those types. Also, I like to define my types as mutually recursive by default, as it makes the code a bit easier to read:

type binop =
    Add of term * term
  | Subtract of term * term
  | Multiply of term * term
  | Divide of term * term
and  term  =
    Const of int
  | Var of string;;

ivg is correct, OCaml only allows you to use functions and types after they've been defined in the program, but as the example above shows, using and in your definitions can override this.

As for this error:

Error: This expression has type binop/1405061 but an expression was expected of type             

binop/1740597

This is actually a side effect of OCaml's immutability: when you reload a file on the TopLevel, you aren't actually changing any of the bindings established the first time you loaded the file, you're just creating new ones. The following example demonstrates this property of OCaml:

utop # let x = 7;;
val x : int = 7
utop # let arg_times_7 arg = arg * x;;
val arg_times_7 : int -> int = <fun>
utop # arg_times_7 6;;
- : int = 42
utop # let x = 6912;;
val x : int = 6912
utop # arg_times_7 6;;
- : int = 42

In short, your code contains lots of errors, that you miss, since you're incrementally sending code into toplevel.

For example, in apply_binop_int you're referring ExprTerm constructor, that is not defined (it will be defined later, but you may refer only to definitions that occurs lexically before). That's why when you load a file, it gives you an error, and apply_binop is not defined. But types are defined. On a second try the apply_binop is defined, because on a previous try the needed types were defined. But as soon, as you define apply_binop you hide expr type with a new definition.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM