在 OCaml 中,我正在处理一个模块和一个仿函数。
我为输入模块和函子设置了签名和结构。然后我使用上面的模块制作了一个新模块。原来我的新模块不包含输入模块中的函数。
我应该能够在我的新模块中使用功能吗?顺便说一句,仿函数中的函数工作正常。另外,我怎样才能确定它是否是一个有效的模块?
在 OCaml 中,我正在处理一个模块和一个仿函数。
我为输入模块和函子设置了签名和结构。然后我使用上面的模块制作了一个新模块。原来我的新模块不包含输入模块中的函数。
我应该能够在我的新模块中使用功能吗?顺便说一句,仿函数中的函数工作正常。另外,我怎样才能确定它是否是一个有效的模块?
让我们举一个真实的例子:
module type MONAD = sig
type 'a t
val return : 'a -> 'a t
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
end
module MonadOps (M : MONAD) = struct
open M (* values from M visible in scope *)
let rec mapM f = function
| [] -> return []
| x::xs ->
f x >>= fun y ->
mapM f xs >>= fun ys ->
return (y :: ys)
end
module Option = struct
type 'a t = 'a option
let return x = Some x
let (>>=) m f = match m with
| None -> None
| Some x -> f x
end
module OptionOps = MonadOps(Option)
let test = OptionOps.mapM
(* val test : ('a -> 'b Option.t) -> 'a list -> 'b list Option.t = <fun> *)
let test = OptionOps.return x
(* Error: Unbound value OptionOps.return *)
functorMonadOps
提供了一些建立在任何 monad 之上的通用特性,但它本身并不包含基本的 monad 特性。它提供了额外的东西,而不包括现有的东西。
您可以通过使用该include
项目将现有模块的内容包含在正在定义的模块值中来更改它:
module MonadOps (M : MONAD) = struct
include M (* values from m *included* in the module *)
let rec mapM f = function
[...]
end
但是,我不一定建议这样做。您正在引入一些在某些情况下很方便的冗余(例如,如果您open
只想一个模块并将所有内容都包含在范围内),但也可能在其他情况下产生一些问题,例如。如果你想组合两个模块扩展函子,你必须想知道以什么顺序应用它们,你可能会遇到奇怪的模块系统黑客。机器学习模块系统内部是复杂的野兽,我建议让你的使用保持简单以避免陷入困境。
请注意,通过不将 M 包含在仿函数中,您可以让仿函数的用户随时选择执行此操作:如果您决定将其直接包含在仿函数中,他们有更多选择。我使用这样一个仿函数的方式是这样的
(* file 'option.ml' *)
type 'a option = None | Some of 'a
module Monad = struct
...
end
module Ops = MonadOps(Monad)
include (Monad : MONAD with type 'a t := 'a option)
include Ops
M
在更简单的设置中,任何由函子中绑定的模块导出的值F
都可以通过使用M.
限定符来访问(就像任何模块一样):
(* out there M does not exist *)
module F (M : sig val v : int end) =
struct
(* in there M is defined *)
let m_v = M.v
end
或open M
在任何访问M
.
module F (M : sig val v : int end) =
struct
open M
let m_v = v
end
如果您想M
从 生成的模块中获取的值F
,您必须以某种方式导出它,或者:
M
直接通过为新模块中的某些值起别名,
module F (...) = struct
let v = M.v
end
通过包含整个模块
module F (...) = struct
include M
end
不一定总是可能或易于使用包含,因为您可能希望定义类型名称与M
.
通过使模块成为新模块的一部分
module F (....) = struct
module M' = M
end
module G = F(struct let v = 5 end);;
print_int G.M.v;;
包括还是不包括?它取决于函子的客户端,以及生成的模块的客户端。您可能在一个程序上工作,该程序必须只知道生成的模块,或者只知道函子。如果模块M
内容是操作模块所必需的G
,它是现成的,那么你必须以M
某种方式提供。