我有:
module Functor(M : sig end) = struct
module NestedFunctor(M : sig end) = struct
end
end
此代码有效:
module V = Functor(struct end)
module W = V.NestedFunctor(struct end)
但这是无效的:
module M = Functor(struct end).NestedFunctor(struct end)
(* ^ Error: Syntax error *)
据我所知,仿函数是一组输入模块和一组允许的输出模块之间的关系。但是这个例子让我理解困惑。为什么functor结果绑定到新模块名称对于调用嵌套函数是必要的?
我的编译器版本= 4.01.0
我是OCaml的新手。当我找到仿函数时,我想象的是
Engine.MakeRunnerFor(ObservationStation
.Observe(Matrix)
.With(Printer))
我认为它是人类友好的架构符号的好工具。 然后我很失望。我明白了:这是语法错误。 我认为这种限制会扩大语法,并使其不那么直观。
而我的“为什么?”主要问题是在语言概念的背景下。
答案 0 :(得分:8)
虽然我不认为这种限制是绝对必要的,但可能是由于OCaml的模块类型系统存在某些限制。在没有涉及太多技术细节的情况下,OCaml要求所有中间模块类型都可以表达为语法签名。但是对于仿函数,这有时是不可能的。例如,考虑:
module Functor(X : sig end) = struct
type t = T of int
module Nested(Y : sig end) = struct let x = T 5 end
end
鉴于此定义,仿函数Functor(struct end).Nested
的类型无法用OCaml语法表示。它需要像
functor(Y : sig end) -> sig val x : Functor(struct end).t end (* not legal OCaml! *)
但Functor(struct end).t
不是OCaml中的有效类型表达式,原因相当技术性(简而言之,允许这样的类型会决定哪些类型相等 - 在类型检查期间必要时 - 更多参与)。
命名中间模块通常可以避免这种困境。给定
module A = Functor(struct end)
仿函数A.Nested
的类型为
functor(Y : sig end) -> sig val x : A.t end
通过引用命名的中间结果A
。
答案 1 :(得分:0)
据我所见,没有深刻的答案。报告的错误是语法错误。即,OCaml的语法并不支持这种表示法。
总结它的一种方法是,在模块表达式的语法中,点总是作为长模块标识符"的一部分出现,即在两个大写标识符之间。我刚刚检查了这个,这就是我所看到的。
答案 2 :(得分:0)
使用manual中的术语,类型等(模块类型,类类型等)可以通过扩展模块路径来限定,其中限定符可以是一个仿函数调用,而非类型(核心表达式,模块表达式,类等)只能通过 module-path 来限定,其中限定符必须是普通的模块名称。
例如,您可以编写类型Functor(struct end).NestedFunctor(struct end).t
但不能编写表达式Functor(struct end).NestedFunctor(struct end).x
或模块表达式Functor(struct end).NestedFunctor(struct end)
。
语法方面,允许表达式中的扩展模块路径是不明确的:表达式F(M).x
被解析为应用于表达式{{1的构造函数F
其中(M).x
是构造函数,M
运算符是记录字段访问运算符。这不会出现类型问题,因为.
显然是M
运算符无法应用的变体,但在解析器中消除它会很复杂。可能还有其他一些我现在没想到的歧义(使用一流的模块?)。
就类型检查器而言,类型指定中的仿函数调用不是问题 - 它们是允许的。然而,论证本身必须是一条道路:你可以写.
而不是Set.Make(String).t
。由于OCaml管理抽象类型的方式,允许类型表达式中的结构和第一类模块会使类型检查器更复杂。每次写Set.Make(struct type t = string let compare = … end).t
时,它都指定相同的抽象类型;但如果你写
Set.Make(String).t
然后module M1 = Set.Make(struct type t let compare = String.compare end)
module M2 = Set.Make(struct type t let compare = String.compare end)
和M1
是不同的抽象类型。制定这个的技术方法是在OCaml中,仿函数应用程序是适用的:将相同的仿函数应用于同一个参数总是返回相同的抽象类型。但结构是生成性的:写M2
两次产生不同的抽象类型 - 所以struct … end
≠Set.Make(struct type t let compare = String.compare end).t
- 生成类型导致类型表达式之间的非自反平等如果你不小心你允许在类型表达式中。
代码生成不会受到太大影响,因为它可以将Set.Make(struct type t let compare = String.compare end).t
视为Functor(struct … end).field
。