我正在尝试在 ocaml 中构建一系列递归模块。这是一个最简单的示例:
module rec Foo : sig
type t =
| A of int
| B of Foo.t
end = struct
type t =
| A of int
| B of Foo.t
end
module rec Bar : sig
open Foo
type map_t = int -> Foo.t
type t = { foo : map_t }
val bar_of_foo : Foo.t -> t
end = struct
open Foo
let rec bar_of_foo f = match f with
| A a -> let ff (_:int) = a in
{ Bar.foo = ff }
| B b -> { foo = bar_of_foo b.foo }
end
在函数 bar_of_foo
中,在 A 的匹配中,编译失败,并出现 错误:未绑定(bind)类型构造函数 f
。
我不明白为什么会发生这种情况 - 字段 foo
被定义为具有类型 map_t = int -> Foo.t
和 f
具有签名 int -> Foo.t
。
我还尝试简单地将记录字段 foo
引用为 foo
而不是 Bar.foo
(在 的匹配情况下>B
- 这给了我一个 Error: unbound record field foo
错误)。
非常感谢您收到的任何指示或建议。
史蒂夫
最佳答案
- 由于
bar_of_foo
是一个函数,因此它的类型为x -> y
。 - 类型推断算法会看到您在
Foo.t
的构造函数上对参数f
进行模式匹配。这一事实修复了x = Foo.t
。 - 当您返回
{foo = ...}
时,系统会获取y = Bar.t
(或者在我们的上下文中只是t
) . - 所以,
bar_of_foo : Foo.t -> Bar.t
。
以下代码可以编译,与OP中的代码类似:
module Foo : sig
type t =
| A of int
| B of t
end = struct
type t =
| A of int
| B of t
end
module Bar : sig
type map_t = int -> Foo.t
type t = { foo : map_t }
val bar_of_foo : Foo.t -> t
end = struct
open Foo
type map_t = int -> Foo.t
type t = { foo : map_t }
let rec bar_of_foo f = match f with
| A a -> { foo = fun (_:int) -> A a }
| B b -> bar_of_foo b
end
测试:
open Foo
open Bar
let () =
let bar = bar_of_foo (B (B (B (A 42)))) in
match (bar.foo 0) with
| A n -> print_int n; print_newline ()
| B _ -> print_string "Impossible!\n"
输出:
$ ocaml example.ml
$ 42
请注意,模块 Foo
和 Bar
不是相互递归的。否则我们就必须写
module rec Foo : sig
...
end = struct
...
end
and Bar : sig
...
end = struct
...
end
关于递归模块中的 OCaml 类型绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37437370/