【问题标题】:OCaml using parametrized moduleOCaml 使用参数化模块
【发布时间】:2023-04-03 20:25:01
【问题描述】:

在 OCaml 中,我处理的是一个模块和一个函子。

我为输入模块和函子设置了签名和结构。 然后我使用上面的模块制作了一个新模块。 原来我的新模块不包含输入模块中的函数。

我应该能够在我的新模块中使用函数吗? 顺便说一句,仿函数中的函数工作正常。 另外,我怎样才能确定它是否是一个有效的模块?

【问题讨论】:

  • 请显示一些源代码。通过一个小代码示例,我们将更好地理解您的问题....

标签: ocaml


【解决方案1】:

我们举一个真实的例子:

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 *)

仿函数MonadOps 提供了一些建立在任何monad 之上的通用特性,但它本身并不包含基本的monad 特性。它提供了额外的东西,而不包括现有的东西。

您可以通过使用 include 项来更改它,将现有模块的内容包含在正在定义的模块值中:

module MonadOps (M : MONAD) = struct
  include M (* values from m *included* in the module *)
  let rec mapM f = function
    [...]
end

但是,我不一定建议这样做。您正在引入一些在某些情况下很方便的冗余(例如,如果您只想open 一个模块并在范围内包含所有内容),但也可能在其他情况下产生一些问题,例如。如果你想组合两个模块扩展函子,你必须想知道以什么顺序应用它们,你可能会遇到奇怪的模块系统黑客。 ML 模块系统内部是复杂的野兽,我建议您保持简单的使用以避免陷入困境。

请注意,通过不将 M 包含在仿函数中,您可以让仿函数的用户随时选择执行此操作:如果您决定将 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

【讨论】:

  • 谢谢你!我明白了。
【解决方案2】:

在更简单的设置中,可以使用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

或在访问M 的值之前使用open 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

【讨论】:

  • 非常感谢您的帮助,didierc。
  • 完成了我的回答,希望能提供更多有用的信息,但恐怕我不能比@gasche 做得更好。
猜你喜欢
  • 2012-07-29
  • 1970-01-01
  • 2017-10-05
  • 1970-01-01
  • 1970-01-01
  • 2017-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多