【问题标题】:Strange Ocaml type error奇怪的 Ocaml 类型错误
【发布时间】:2015-08-06 14:56:46
【问题描述】:

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

【问题讨论】:

    标签: types ocaml calculator variant


    【解决方案1】:

    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
    

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 所以我重新排列了代码,以便首先进行所有类型声明,这似乎解决了这个问题。这是您所指的唯一错误还是还有其他错误?
      猜你喜欢
      • 2014-05-09
      • 1970-01-01
      • 1970-01-01
      • 2016-07-19
      • 2013-12-16
      • 2016-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多