为多态变体类型编写类型签名时,可以使用类型变量吗?

时间:2019-03-09 09:52:55

标签: ocaml

我想将类型变量约束为仅允许多态变体类型,以便我可以使用该变量在签名中构造其他多态变体类型:

type 'a t
val f : 'a t -> [`Tag | 'a] t

在OCaml中有没有办法做到这一点?也许改用类/对象?天真的尝试未能编译:

type 'a t = { dummy: int } constraint 'a = [>]

let f : 'a t -> ['a | `Tag] t = fun _ -> { dummy = 0 }
                 ^^

The type [> ] does not expand to a polymorphic variant type

问题的原因:

我想使用类型签名来静态地反映t的功能,以强制没有给定功能的t永远不能不适当地使用。

val do_something_cool : [<`Super_power] t -> unit
val do_something_else : [<`Super_power|`Extra_super_power] t -> unit
val enhance : 'a t -> ['a | `Super_power] t
val plain_t : [`Empty] t

let () = plain_t |> do_something_cool (* fails *)
let () = plain_t |> enhance |> do_something_cool (* succeeds *)
let () = plain_t |> enhance |> do_something_else (* succeeds *)

显然,还有其他方法可以实现这种编译时安全性。例如,enhance可以只返回一个[`Super_power] t,可以在需要时代替plain_t。但是,我真的很好奇第一种方法能否成功。我正在写一个DSL,如果t的所有功能都可以反映在它的类型中,它将更加简洁。

1 个答案:

答案 0 :(得分:2)

简短的回答是“否”:只能内联类型声明,而不是类型变量。换句话说,这很好:

type on = [`On]
type off = [`Off]
type any = [ on | off ]
let f: [< any ] -> _ = fun _ -> ()

但不是这个

let merge: 'a -> 'b -> [ 'a | 'b ] = ...

但是,如果您只有一组封闭的独立功能,则可以切换到对象幻像类型,其中每个容量对应一个字段,并且每个字段可以是onoff 。例如,

type +'a t constraint 'a = < super: [< any ]; extra: [< any ]>

然后只需要功能结合的使用者函数就相对容易编写:

val do_something_cool : < super:on; ..>  t -> unit
val do_something_extra : < extra:on; ..> t -> unit
val do_something_super_but_not_extra: <super:on; extra:off; .. > t -> unit

但是切换功能onoff更为复杂,并且会修复功能集:

val enhance : < super: _; extra: 'es > t -> < super: on; extra:'es > t

除了这些限制,一切都按预期进行。例如,如果我有一个变量x

 val x: <super: off; extra:on > t

这有效:

 let () = do_something_extra x

 let () = do_something_cool x

失败,最后

 let () =
   let x = enhance x in
   do_something_cool x; do_something_extra x

也可以。

因此,主要问题是编写启用功能。一种可能有用的技巧是 编写帮助程序类型以更轻松地操作功能的子集。 例如,如果我有一个复杂的类型:

type 'a s
  constraint 'a = < a: [< any]; b:[< any]; c: [< any ]; d: [< any] >

我可以使用以下类型:

type ('a, 'others) a = < a:'a; b:'b; c:'c; d: 'd>
  constraint 'others = 'b * 'c * 'd

选择功能a,然后进行写

val enable_a: (_,'rest) a s -> (on, 'rest) a s

无需显式隐藏在'rest中的三个类型变量。