奇怪的 Ocaml 类型错误

Strange Ocaml type error

我正在编写一个 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

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

例如,在 apply_binop_int 中,您指的是未定义的 ExprTerm 构造函数(稍后将对其进行定义,但您可以仅引用之前词法上出现的定义)。这就是为什么当你加载一个文件时,它给你一个错误,并且 apply_binop 没有定义。但是类型是有定义的。第二次尝试定义了 apply_binop,因为在之前的尝试中定义了所需的类型。但是一旦你定义了 apply_binop ,你就用新的定义隐藏了 expr 类型。

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 上重新加载文件时,您实际上并没有更改第一次加载文件时建立的任何绑定,您只是在创建新的绑定.以下示例演示了 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