我想将类型变量约束为仅允许多态变体类型,以便我可以使用该变量在签名中构造其他多态变体类型:
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
的所有功能都可以反映在它的类型中,它将更加简洁。
答案 0 :(得分:2)
简短的回答是“否”:只能内联类型声明,而不是类型变量。换句话说,这很好:
type on = [`On]
type off = [`Off]
type any = [ on | off ]
let f: [< any ] -> _ = fun _ -> ()
但不是这个
let merge: 'a -> 'b -> [ 'a | 'b ] = ...
但是,如果您只有一组封闭的独立功能,则可以切换到对象幻像类型,其中每个容量对应一个字段,并且每个字段可以是on
或off
。例如,
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
但是切换功能on
或off
更为复杂,并且会修复功能集:
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
中的三个类型变量。