【问题标题】:OCaml recursive modules across compilation units跨编译单元的 OCaml 递归模块
【发布时间】:2016-02-02 10:59:31
【问题描述】:

我正在尝试将以下递归模块拆分为单独的编译单元。具体来说,我希望 B 在自己的 b.ml 中,以便能够与其他 A 重用它。

module type AT = sig
  type b
  type t = Foo of b | Bar
  val f : t -> b list
end

module type BT = sig
  type a
  type t = { aaa: a list; bo: t option }
  val g : t -> t list
end

module rec A : (AT with type b = B.t) = struct
  type b = B.t
  type t = Foo of b | Bar
  let f = function Foo b -> [ b ] | Bar -> []
end
and B : (BT with type a = A.t) = struct
  type a = A.t
  type t = { aaa: a list; bo: t option }
  let g b =
    let ss = List.flatten (List.map A.f b.aaa) in
    match b.bo with
    | Some b' -> b' :: ss
    | None -> ss
end

let a = A.Bar;;
let b = B.({ aaa = [a]; bo = None });;
let c = A.Foo b;;
let d = B.({ aaa = [a;c]; bo = Some b });;

我不知道如何跨单元拆分它。

来自 Xavier Leroy 的 paper 关于该主题的以下句子让我希望可以使用 OCaml 的模块语法进行编码:“该提案不支持编译单元之间的递归。但是后者可以使用单独编译的仿函数进行编码,其固定点稍后会使用模块 rec 构造”。

我玩过模块 rec,但似乎无法找到一种方法来对其进行类型检查。在 B 的函数 g 中使用 A 的函数 f 似乎会造成麻烦。

(对于上下文,在原始代码中At是指令类型,Bt是基本块类型。分支指令引用块,块包含指令列表。我想重用基本块类型和关联具有不同指令集的函数。)

【问题讨论】:

  • 如果我下面的回答不能解决您在拆分此文件时遇到的问题,请在单独编译时发布您迄今为止的最佳尝试,以及您在A.f 中遇到的类型错误B.g.
  • 另外,关注你问题的结尾,还有一些其他方法可以解决这个问题——例如,将基本块索引或键存储到数据结构中,而不是键入基本块引用,在您的指示。
  • 谢谢antron,在尝试做出最佳尝试时,我显然偶然发现了一个解决方案,至少对于这个测试用例是这样。希望它能转化为我的实际代码。
  • 好,祝你好运:)

标签: recursion module ocaml functor


【解决方案1】:

以下似乎可行,尽管它相当难看。

(* asig.mli *)

module type AT = sig
  type b
  type b' (* b = b' will be enforced externally *)
  type t
  val f : t -> b' list
end

(* bsig.mli *)

module type BT = sig
  type a
  type b' (* t = b' will be enforced externally *)
  type t = { aaa: a list; bo: b' option }
  val g : t -> b' list
end

(* b.ml *)

open Asig

module MakeB(A : AT) = struct
  type a = A.t
  type t = { aaa: a list; bo: A.b' option }
  type b' = A.b'
  let g b =
    let ss = List.flatten (List.map A.f b.aaa) in
    match b.bo with
    | Some b' -> b' :: ss
    | None -> ss
end

(* a.ml *)

open Asig
open Bsig

module type ASigFull = sig
  type b
  type b'
  type t = Foo of b | Bar
  val f : t -> b' list
end

module type BMAKER = functor (A : AT) -> (BT with type a = A.t
                                              and type b' = A.b')
module MakeAB(MakeB : BMAKER) = struct

module rec B1 : (BT with type a = A1.t
                     and type b' = A1.b') = MakeB(A1)
       and A1 : (ASigFull with type b = B1.t
                           and type b' = B1.t) = struct
  type b = B1.t
  type b' = b
  type t = Foo of b | Bar
  let f = function Foo b -> [ b ] | Bar -> []

end

module A = (A1 : ASigFull with type t = A1.t and type b = B1.t and type b' := B1.t)
module B = (B1 : BT with type t = B1.t and type a = A1.t and type b' := B1.t)

end

module AB = MakeAB(B.MakeB)
module A = AB.A
module B = AB.B

let a = A.Bar;;
let b = B.({ aaa = [a]; bo = None });;
let c = A.Foo b;;
let d = B.({ aaa = [a;c]; bo = Some b });;

【讨论】:

    【解决方案2】:

    我认为这篇论文指的是这样的东西:

    (* a.ml *)
    
    module F (X : sig val x : 'a -> 'a end) =
    struct
      let y s = X.x s
    end
    

    (* b.ml *)
    
    module F (Y : sig val y : 'a -> 'a end) =
    struct
      (* Can use Y.y s instead to get infinite loop. *)
      let x s = Y.y |> ignore; s
    end
    

    (* c.ml *)
    
    module rec A' : sig val y : 'a -> 'a end = A.F (B')
           and B' : sig val x : 'a -> 'a end = B.F (A')
    
    let () =
      A'.y "hello" |> print_endline;
      B'.x "world" |> print_endline
    

    运行此 (ocamlc a.ml b.ml c.ml && ./a.out) 打印

    hello
    world
    

    显然,我使用的A 和B 的定义是无稽之谈,但您应该能够将您自己的定义替换为此模式,以及使用命名签名而不是像我一样将它们写出来。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多