参数化仿函数
Parameterized functors
假设我有这些签名:
module type CharS = sig
type c
type t = BoW | C of c | EoW
val compare : t -> t -> int
val print : Format.formatter -> t -> unit
end
module type GraphemS = sig
type c
type t
val compare : t -> t -> int
val print : Format.formatter -> t -> unit
end
还有这两个函子:
module MakeDiGraphem (C : CharS) : GraphemS with type c = C.t = struct
type c = C.t
type t = c * c
let compare (cb1, ce1) (cb2, ce2) =
let r1 = C.compare cb1 cb2 in
if r1 = 0 then
C.compare ce1 ce2
else r1
let print fmt (cb, ce) =
Format.fprintf fmt "@[%a%a@]@," C.print cb C.print ce
end
module MakeMonoGraphem (C : CharS) : GraphemS with type c = C.t = struct
type c = C.t
type t = c
let compare c1 c2 = C.compare c1 c2
let print fmt c =
Format.fprintf fmt "@[%a@]@," C.print c
end
现在,我想要一个仿函数,它允许我用第一个或第二个仿函数创建一个 GraphemS 类型的模块。我所做的是这样的:
module type SizeS = sig
type t = int
val param : int
end
module MakeGraphem (C : CharS) (I : SizeS) : GraphemS with type c = C.t = struct
module MonoGraphem = MakeMonoGraphem(C)
module DiGraphem = MakeDiGraphem(C)
let select_graphem =
if I.param = 1 then
(module MonoGraphem : GraphemS)
else
(module DiGraphem : GraphemS)
include (val select_graphem)
end
但遗憾的是我得到了:
Error: This expression creates fresh types.
It is not allowed inside applicative functors.
那么我的问题是,是否可以做我想做的事,这个错误是什么意思?
基本上,您不能在 applicative functor 应用程序 include 中进行 first-class 计算。基本上,类型系统不能保证 I.param
是一个常量,所以它不能确保仿函数总是 return 相同的类型。应用仿函数(OCaml 中的默认值)必须始终 return 同一表达式的相同类型(从某种意义上说,它是纯粹的)。
如果您使用的是 OCaml 4.02 或更高版本,您可以通过 unit argument:
将仿函数声明为生成函数
module MakeGraphem (C : CharS) (I : SizeS) () :
GraphemS with type c = C.t = struct
我最喜欢的方法是让仿函数作为参数应用而不是 I
。
假设我有这些签名:
module type CharS = sig
type c
type t = BoW | C of c | EoW
val compare : t -> t -> int
val print : Format.formatter -> t -> unit
end
module type GraphemS = sig
type c
type t
val compare : t -> t -> int
val print : Format.formatter -> t -> unit
end
还有这两个函子:
module MakeDiGraphem (C : CharS) : GraphemS with type c = C.t = struct
type c = C.t
type t = c * c
let compare (cb1, ce1) (cb2, ce2) =
let r1 = C.compare cb1 cb2 in
if r1 = 0 then
C.compare ce1 ce2
else r1
let print fmt (cb, ce) =
Format.fprintf fmt "@[%a%a@]@," C.print cb C.print ce
end
module MakeMonoGraphem (C : CharS) : GraphemS with type c = C.t = struct
type c = C.t
type t = c
let compare c1 c2 = C.compare c1 c2
let print fmt c =
Format.fprintf fmt "@[%a@]@," C.print c
end
现在,我想要一个仿函数,它允许我用第一个或第二个仿函数创建一个 GraphemS 类型的模块。我所做的是这样的:
module type SizeS = sig
type t = int
val param : int
end
module MakeGraphem (C : CharS) (I : SizeS) : GraphemS with type c = C.t = struct
module MonoGraphem = MakeMonoGraphem(C)
module DiGraphem = MakeDiGraphem(C)
let select_graphem =
if I.param = 1 then
(module MonoGraphem : GraphemS)
else
(module DiGraphem : GraphemS)
include (val select_graphem)
end
但遗憾的是我得到了:
Error: This expression creates fresh types.
It is not allowed inside applicative functors.
那么我的问题是,是否可以做我想做的事,这个错误是什么意思?
基本上,您不能在 applicative functor 应用程序 include 中进行 first-class 计算。基本上,类型系统不能保证 I.param
是一个常量,所以它不能确保仿函数总是 return 相同的类型。应用仿函数(OCaml 中的默认值)必须始终 return 同一表达式的相同类型(从某种意义上说,它是纯粹的)。
如果您使用的是 OCaml 4.02 或更高版本,您可以通过 unit argument:
将仿函数声明为生成函数module MakeGraphem (C : CharS) (I : SizeS) () :
GraphemS with type c = C.t = struct
我最喜欢的方法是让仿函数作为参数应用而不是 I
。