Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/mercurial/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Types OCaml递归类型交叉a";模块类型=";_Types_Module_Ocaml - Fatal编程技术网

Types OCaml递归类型交叉a";模块类型=";

Types OCaml递归类型交叉a";模块类型=";,types,module,ocaml,Types,Module,Ocaml,我有一套复杂的约束条件(主要是教学方面的约束),导致我想做这样的事情: type alpha = ... ENV.env ... and module type ENV = sig type env val foo : ...alpha... end module E1 : M1.ENV = struct module type TMP = sig type tmp_beta (* or some other type from Virtual, whic

我有一套复杂的约束条件(主要是教学方面的约束),导致我想做这样的事情:

type alpha = ... ENV.env ...
and module type ENV = sig
    type env
    val foo : ...alpha...
end
  module E1 : M1.ENV = struct
   module type TMP = sig
      type tmp_beta (* or some other type from Virtual, which we can't include until after declaring env *)
      type env = int -> tmp_beta
      include M1.Virtual with type env2 = env
    end
    (* now we unify tmp_beta with Virtual.beta *)
    module rec TMP : TMP with type tmp_beta = TMP.beta = TMP
    include TMP
  end (* E1 *)
但这是不合法的。首先,您不能将
模块类型ENV=
作为递归类型定义的一部分。(我认为甚至没有任何版本的递归声明仅限于模块类型声明。)其次,您不能让
ENV.ENV
调用模块类型的组件;您只能编写
M.env
,其中
M
是一个已实现的模块结构。但是,如果像上面这样的事情是合法的,它将抓住我的目标

这是一个简化的测试用例,展示了我的一些限制

(* M1 is really the contents of an .mli file, not a module *)
module M1 = struct
  type env (* I want env to be opaque or abstract at this point ... *)
  type alpha = A of int | B of env * int
  type beta = C of int | D of alpha
  module type ENV = sig
    type env (* ...but to be unified with this env *)
    val foo : unit -> beta -> unit
    val empty : env
  end
end

(* M2 needs to be in another_file.ml *)
module M2 = struct
  (* here we provide an implementation of M1.ENV *)
  module E1 : M1.ENV = struct
    type env = char
    let foo () _ = ()
    let empty = 'X'
  end

  (* then I'd want this to typecheck, but it doesn't *)
  let _ = M1.B(E1.empty, 0)
end
M1
中,
ENV
声明之前的部分需要引用
ENV
类型,但是
ENV
声明本身需要引用
M1
的其他部分中发生的一些情况。所以不清楚哪一个应该放在第一位。如果
ENV
不需要引用
beta
,而beta又引用
alpha
,我可以将
ENV
放在文件的开头,并
包含ENV
(正如我上面所说,这实际上是一个.mli文件),以便访问
ENV
类型以声明
alpha
。我不确定这是否真的会导致
alpha
中的
env
M1中的
env
相同。env
(在OCaml
include
-ing中,模块类型被称为其内容的文本副本);但无论如何,我不能在这里做,因为相互依赖。因此,我必须在
M1
的开头预先声明
类型env
。对于我的需求来说,我们不能在
M1
中指定
env
的实现,这一点至关重要

上面我为
M1
给出的声明被OCaml接受,但这两种类型
env
并不统一。这并不奇怪,但我的任务是找到一些能将它们统一起来的曲解。当我们稍后为
ENV
提供一个实现时,就像上面的
M2
一样,我们希望能够使用它来提供
M1.alpha
的实例。但目前我们没有:
M1.B(E1.empty,0)
不会进行打字检查

现在有一个解决办法。我可以使用
M1.alpha
使用类型变量
'env
而不是抽象类型
env
。但是,
M1.alpha
需要在
'env
上进行参数化,
M1.beta
也需要进行参数化,并且由于我的类型之间的相互依赖性,整个项目中几乎每种类型都需要在
'env
类型上进行参数化,这是一个具体的例子,在我们到达建筑链更深处的
模块M2
之前,我们无法提供它。这在教学上是不可取的,因为它使所有类型都更难理解,即使在环境没有直接相关性的情况下也是如此


因此,我一直在试图弄清楚,是否有一些技巧可以通过函子或一级模块来实现,这将使我能够获得我在
模块M1
中寻找的那种相互依赖关系,并在稍后的文件中提供
env
类型的实现,这里由
模块M2
表示。我还没有弄明白这样一件事。

我不知道这是否有帮助,但这个小例子对我很有用:

# module rec A : sig type alpha = B.env list end = A    
  and B : sig type env val foo: A.alpha end =
      struct type env = int let foo = [3] end;;
module rec A : sig type alpha = B.env list end
and B : sig type env val foo : A.alpha end
# B.foo
- : A.alpha = [<abstr>]
#模块rec A:sig type alpha=B.env list end=A
和B:sig-type-env-val-foo:A.alpha-end=
结构类型env=int let foo=[3]end;;
模块记录A:sig类型alpha=B.env列表结束
和B:sig-type-env-val-foo:A.alpha-end
#B.foo
-:A.alpha=[]

它的结构似乎让人想起您最初的示例,限制了
alpha
最终被包装在模块中。

正如我在评论中所说,@Jeffrey Scofield的回答向我揭示了使用递归模块定义在模块实现中重复模块sig的纯类型部分的好技巧,无需重复。这和一点思考为我的测试用例提供了以下解决方案。这是一个解决方案,这取决于我对构建链中
M2
的位置有一定的灵活性,并且我愿意将
M1.ENV
作为
M1
其余签名的扩展,并让包中的其他文件使用
M2
提供的实现,而不是使用
M1
。这些都符合我的实际约束条件

(* M1 is really the contents of an .mli file, not a module *)
module M1 = struct
  type env (* I want env to be opaque or abstract at this point ... *)
  type alpha = A of int | B of env * int
  type beta = C of int | D of alpha
  module type ENV = sig
    type env (* ...but to be unified with this env *)
    val foo : unit -> beta -> unit
    val empty : env
  end
end

(* M2 needs to be in another_file.ml *)
module M2 = struct
  (* here we provide an implementation of M1.ENV *)
  module E1 : M1.ENV = struct
    type env = char
    let foo () _ = ()
    let empty = 'X'
  end

  (* then I'd want this to typecheck, but it doesn't *)
  let _ = M1.B(E1.empty, 0)
end
诀窍在于做到这一点:

(* M1 is really the contents of an .mli file, not a module *)
module M1 = struct
  (* we encapsulate the prefix of M1 in its own sig *)
  module type Virtual = sig
    type env2 (* an abstract type for now ... *)
    type alpha = A of int | B of env2 * int
    type beta = C of int | D of alpha
  end
  module type ENV = sig
    type env
    (* Now we include Virtual inside ENV, unifying their types
       using the standard OCaml method. This makes ENV an
       extension of the other parts of M1, rather than a small
       standalone sig. But that's OK; see below. *)
    include Virtual with type env2 = env
    (* now beta is available *)
    val foo : unit -> beta -> unit
    val empty : env
  end
end (* M1 *)

(* M2 needs to be in another_file.ml *)
module M2 = struct
  (* here we provide an implementation of E1 *)
  module E1 : M1.ENV = struct
    type env = char
    let foo () _ = ()
    let empty = 'X'
    (* Here's how we can easily provide all the rest of ENV
       that we're now obliged to provide. *)
    module rec MX : M1.Virtual with type env2 = env = MX
    include MX
  end
  (* this should be legitimate, and it is! *)
  let _ = E1.B(E1.empty, 0)
end (* M2 *)
编辑:在我的实际用例中,我想让类型
env
的实现使用
M1.Virtual
中的其他类型。我最终需要做这样的事情:

type alpha = ... ENV.env ...
and module type ENV = sig
    type env
    val foo : ...alpha...
end
  module E1 : M1.ENV = struct
   module type TMP = sig
      type tmp_beta (* or some other type from Virtual, which we can't include until after declaring env *)
      type env = int -> tmp_beta
      include M1.Virtual with type env2 = env
    end
    (* now we unify tmp_beta with Virtual.beta *)
    module rec TMP : TMP with type tmp_beta = TMP.beta = TMP
    include TMP
  end (* E1 *)

这是一个非常多的扭曲。但它似乎奏效了。我在这里添加了这项技术,以防其他人可能也需要在OCaml中“向前声明”类型,但由于某种原因,通过通常的递归类型声明无法这样做——就像我需要跨越
模块类型=
障碍一样。

谢谢,是的,使用递归模块是我给出的玩具示例的一个选项,但事实并非如此。其中一个原因是这些文件位于不同的文件中。另一个原因是M1实际上是一个.mli文件的主体,所以它是一个模块类型,而不是一个模块。但在我的玩具示例中,我看不到如何将M1声明为模块类型,但仍然在M2中引用其组件。但是你可以对编译的.mli文件这样做。一个真正的模块专家会比我更有帮助。但也许您可以在一个地方定义递归类型,然后在
.mli
文件中调用它们?我以前做过(结构比较简单),但是你的回答告诉我你可以做
模块rec A:sig。。。结束=A