奇怪的Ocaml类型错误

时间:2015-08-06 14:56:47

标签: types ocaml calculator variant

我正在编写一个ocaml程序来处理基本的算术命令和符号数学命令。但是,代码目前给我一个奇怪的类型错误。我觉得这可能会出现,因为有两种不同的变种使用了binop类型,但我不确定。

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

我得到的错误:

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

2 个答案:

答案 0 :(得分:2)

OCaml约定总是定义类型,然后定义对这些类型进行操作的函数。另外,我喜欢默认将我的类型定义为相互递归,因为它使代码更容易阅读:

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是正确的,OCaml只允许您在程序中定义后使用函数和类型,但正如上面的示例所示,在您的定义中使用and可以覆盖它。

至于这个错误:

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

binop / 1740597

这实际上是OCaml不可变性的副作用:当你在TopLevel上重新加载文件时,你实际上并没有改变你第一次加载文件时建立的任何绑定,你是&#39 ;只是创造新的。以下示例演示了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

答案 1 :(得分:1)

简而言之,您的代码包含许多错误,您错过了,因为您正在逐步将代码发送到顶层。

例如,在apply_binop_int中你指的是ExprTerm构造函数,它没有定义(它将在后面定义,但你可能只会引用以前词汇发生的定义)。这就是为什么当您加载文件时,它会给您一个错误,并且未定义apply_binop。但是类型是定义的。第二次尝试定义了apply_binop,因为在之前的尝试中定义了所需的类型。但是,在您定义apply_binop时,您会立即隐藏expr类型并使用新定义。