首页 文章

Ocaml抽象类型和类型推断

提问于
浏览
2

我对OCaml中的抽象类型有疑问 .

假设我有一个隐藏某种类型的模块:

module Test : sig
  type t
  val make_t : unit -> t
end = struct
  type t = int option
  let make_t () = Some 42
end

而且我还有一个对选项进行操作的函数:

let do_work : 'a option -> unit = function
  | Some x -> Printf.printf "Some\n"
  | None   -> Printf.printf "None\n"

毫不奇怪,当我在t实例上调用do_work时出现类型错误:

let () =
  do_work @@ Test.make_t ()

错误:此表达式的类型为Test.t,但表达式的类型为'a option

在我的应用程序中,我比int选项更复杂,我不想向外暴露它的内部 . 但是,我想告诉OCaml编译器t实际上是一个选项 . 我怎样才能做到这一点?

5 回答

  • 1

    或者您可以提供抽象以允许正确类型的函数应用

    module Test : sig
      type t
      val apply  : (int option -> 'a) -> t -> 'a
      val make_t : unit -> t
      val ( @ )  : (int option -> 'a) -> t -> 'a
    end = struct
      type t = int option
      let make_t () = Some 42
      let apply f x = f x
      let ( @ ) = apply
    end
    
    let do_work : 'a option -> unit = function
      | Some x -> Printf.printf "Some\n"
      | None   -> Printf.printf "None\n"
    
    let () =
      let open Test in
      do_work @ make_t ()
    

    操作员可能有点太多但看起来更酷 . 另外,您可能需要查看monad .

  • 4

    最直接的方法是只使选项的内容类型为abstract:

    module Test :
    sig
      type t'
      type t = t' option
      val make_t : unit -> t
    end =
    struct
      type t' = int
      type t = t' option
      let make_t () = Some 42
    end
    
  • 4

    如果您不想公开您的实现,但是您需要使类型与其他类型兼容,请编写函数以转换为这些类型/从这些类型转换:

    module Test : sig
      type t
      val make_t : unit -> t
      val to_option : t -> int option (* Signature *)
    end = struct
      type t = int option
      let make_t () = Some 42
      let to_option t = t (* Implementation *)
    end
    

    然后,当你特别需要 int option 时:

    let () =
      Test.make_t ()
      |> Test.to_option
      |> do_work
    

    这样,您的类型 Test.t 保持抽象,这意味着您可以在不更改接口的情况下更改实现,前提是您在转换器中执行必要操作以保持其一致性 .

  • 3

    看来你想要的是使 t 成为私人类型:

    module T : sig
      type t = private int option
      val mk: unit -> t
    end = struct
      type t = int option
      let mk () = Some 42
    end
    
    let do_work: int option -> unit =
      function
      | None -> print_endline "None"
      | Some i -> print_endline (string_of_int i)
    
    let () =
      do_work (T.mk () :> int option)
    

    与抽象类型解决方案一样,它允许对 t 的实例的创建进行一些控制,但允许解构此类实例(封装可防止) .

  • 2

    除了这里已有的好答案,如果您只关心某些东西是 Some _ 还是 None ,您可以将其添加到您的界面:

    module Test : sig
      type t
      val make_t : unit -> t
      val is_some : t -> bool
    end = struct
      type t = int option
      let make_t () = Some 42
      let is_some = function
        | Some _ -> true
        | None -> false
    end
    

    (当然你可以将 is_some 重命名为与你的抽象相匹配的东西)

相关问题