OCaml recursive modules across compilation units

纵然是瞬间 提交于 2020-01-02 10:09:12

问题


I'm trying to split the following recursive modules into separate compilation units. Specifically, I'd like B to be in its own b.ml, to be able to reuse it with other A's.

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 });;

I can't figure out how to split it across units.

The following sentence from Xavier Leroy's paper on the topic gives me hope that it's possible to encode using OCaml's module syntax: "the proposal does not support recursion between compilation units. The latter can however be encoded using separately-compiled functors, whose fix-point is taken later using the module rec construct".

I've played around with module rec but can't seem to find a way to make it type-check. The use of A's function f inside B's function g seems to cause the trouble.

(For the context, in the original code A.t is an instruction type, and B.t is a basic block type. Branch instructions reference blocks, and blocks contain lists of instructions. I'd like to reuse the basic block type and associated functions with different instruction sets.)


回答1:


I think the paper is referring to something like this:

(* 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

Running this (ocamlc a.ml b.ml c.ml && ./a.out) prints

hello
world

Obviously, the definitions of A and B I used are nonsense, but you should be able to substitute your own definitions into this pattern, as well as use named signatures instead of writing them out literally like I did.




回答2:


The following seems to work, although it is rather ugly.

(* 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 });;


来源:https://stackoverflow.com/questions/33479679/ocaml-recursive-modules-across-compilation-units

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!