编辑:我用模块类型 A 和 B 替换了抽象示例,用一个更具体的示例使用组和环。
我在一个使用众所周知的代数结构的例子中提出了我的仿函数问题。为组定义签名:
module type Group =
sig
type t
val neutral: t
val add: t -> t -> t
val opp: t -> t
end
并为包含许多有用操作的组定义签名,例如这里共轭:
module type Extended_Group =
sig
include Group
val conjugate: t -> t -> t
end
共轭的实现只取决于加法和相反,所以我不想为我定义的所有组明确地写它,所以我写了以下仿函数:
module F (G: Group) =
struct
include G
let conjugate x y = add (add x y) (opp x)
end
现在,假设您正在使用“扩展”组概念的其他结构,例如整数环:
module Z =
struct
(* Group *)
type t = int
let neutral = 0
let add x y = x+y
let opp x = -x
(* Ring *)
let m_neutral = 1
let mult x y = x*y
end
由于环是群的粒子情况,我们可以对它应用仿函数 F:
module Extended_Z = F(Z)
不幸的是,为了将 F 应用到 Z,Ocaml 首先将 Z 的类型限制为 Group,然后再应用 F。
一切的行为就像我这样做一样:
module (Group_Z: Group) = Z
module Extended_Z = F(Group_Z)
因此,尽管 Z.mult 有意义,但 Extended_Z.mult 对编译器来说是未知的。我不想失去 Extended_Z 仍然是戒指的事实!当然,包含编写一个仿函数 F_Ring 的解决方案与 F 一样,但将 Ring 作为输入并不是一个令人满意的解决方案:我不想编写 F_Fields、F_VectorSpace 等。
这是我想要的:
可以用另一种方式来做,限制模块的签名(使用我用于 Group_Z 的语法),但我找不到放大签名的方法。这里会是这样的:
module (Ring_Extended_Z: Ring) = Extended_Z
签名“至少组”并输出签名模块“至少扩展组”。当然,
这仅在存在 include 时才有意义,这就是为什么我坚信 OCaml 中不存在这样的功能。
在网上找了几天的答案后,我认为正确的实现方法其实是以下一种:
(灵感来自 Real World OCaml 的第 9 章)
module F (G: Group) =
struct
let conjugate x y = ...
end
(与以前相同,但没有包含)
然后在同一点执行所有包含:
module Extended_Z =
struct
include Z
include F(Z)
end
有人可以确认这是实现我想要的好方法,而且我的第一种方法不适合
OCaml 的精神?更具体地说,有人可以确认选项 1. 和 2. 在 OCaml 中确实是不可能的吗?
编辑:关于动态类型
在我看来,这仍然是静态类型,因为如果我写
module Extended_Z = F(Z: Ring)
OCaml 仍然可以将其作为具有额外值“共轭”的签名环的模块静态键入。它需要模块级别的多态性,
我理解不存在,但我认为它可以存在而不使类型系统动态化。
最佳答案
我在这个答案中分享了我发现的两个部分令人满意的解决方案来实现我想要的。
第一个是我的问题末尾建议的那个,在代数结构的情况下可能是“好”的一个。
module type Group =
sig
type t
val neutral: t
val add: t -> t -> t
val opp: t -> t
end
module type Extended_Group =
sig
include Group
val conjugate: t -> t -> t
end
module F (G: Group) =
struct
let conjugate x y = G.add (G.add x y) (G.opp x)
end
module type Ring =
sig
include Group
val m_neutral: t
val mult: t -> t -> t
end
module Z =
struct
type t = int
let neutral = 0
let add x y = x+y
let opp x = -x
(* Ring *)
let m_neutral = 1
let mult x y = x*y
end
module Extended_Z =
struct
include Z
include F(Z)
end
在这种情况下,由 OCaml 推断的 Extended_Z 的签名“扩展”了签名 Ring,并且确实 Extended_Z.mult 在模块外部是可见的。
第二种解决方案使用 Andreas Rossberg 和 Ivg 建议的部分来模拟模块级别的多态性。在代数结构的情况下它不太令人满意,但对于我的实际问题来说它是完美的。
module type Group_or_More =
sig
include Group
module type T
module Aux: T
end
module F (G: Group_or_More) =
struct
include G
let conjugate x y = add (add x y) (opp x)
end
module Z =
struct
type t = int
let neutral = 0
let add x y = x + y
let opp x = -x
module type T =
sig
val m_neutral: t
val mult: t -> t -> t
end
module Aux =
struct
let m_neutral = 1
let mult x y = x*y
end
end
module Extended_Z = F(Z) ;;
在这里,签名 Group_or_More 准确地捕捉到了我对签名扩展另一个签名的想法。你把所有额外的东西都放在辅助模块 Aux 中。但是,为了能够提供尽可能多的辅助功能,您需要将此 Aux 模块的签名指定为您的组的一部分。
对于代数结构,它不太令人满意,因为 Z 和 Extended_Z 是较弱意义上的环:乘法由 Z.Aux.mult 和 Extended_Z.Aux.mult 访问。
关于include - OCaml 中的多态仿函数(与 Include 命令相关),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38100250/