我正在研究 Andrew Appel 撰写的《ML 中的现代编译器实现》的第 1 章,我决定用 OCaml 而不是 SML 来实现它。我是 OCaml 新手,遇到了一个非常令人沮丧的问题。OCaml 似乎认为下面的函数具有签名int * (int * 'a) -> 'a option。
let rec lookupTable = function
| name, (i, v) :: _ when name = i -> Some v
| name, (_, _) :: rest -> lookupTable (name, rest)
| _, [] -> None
Run Code Online (Sandbox Code Playgroud)
但据我所知,应该没有任何迹象表明元组中的第一个元素是 int。这是一个问题,因为当lookupTable函数运行时,编译器会抱怨我没有向它传递一个整数。也许我错过了一些非常明显的东西,但它非常令人难以置信。这是程序的其余部分
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)
Run Code Online (Sandbox Code Playgroud)
Base 定义=为int -> int -> bool,因此当您有表达式时,name = i编译器会将它们推断为ints。
您可以通过模块访问多态函数和运算符Poly,或者通过本地打开相关模块来使用特定于类型的运算符,例如String.(name = i).
Base 默认情况下不公开多态运算符的原因在文档的简介中进行了简要解释:
OCaml 标准库公开的比较运算符是多态的:
他们实现的是值的运行时表示的结构比较。由于这些通常容易出错,即它们与用户期望的不符,因此它们不会由 Base 直接公开。
还有一个性能争论,因为多态/结构运算符还需要在运行时检查它是什么类型的值,以便正确地比较它们。