是否有可能明确写下非多态但延迟统一的类型,如下划线类型?
因此,OCaml有时会在类型检查过程中生成顶级打印带有前导下划线(例如_a
)的类型。具体来说,这些在实例化空Hashtbl.t
时和其他一些情况下会出现。
# Hashtbl.create 1;;
- : ('_a, '_b) Hashtbl.t = <abstr>
但是,用户无法在源代码中明确引用这些类型。
# (5: int);;
- : int = 5
# (5: 'a);;
- : int = 5
# (5: '_a);;
Error: The type variable name '_a is not allowed in programs
您可以通过利用OCaml中缺少更高级别的多态来创建一个显式非多态函数
# let id = snd ((), fun y -> y);;
val id : '_a -> '_a = <fun>
# (fun () -> fun y -> y) ();;
- : '_a -> '_a = <fun>
我希望能够做类似
的事情let id : <some magical type> = fun x -> x
并且不依赖于可能在未来可能会消失的类型系统的限制。
答案 0 :(得分:2)
您可以使用引用不可推广的事实。
# let id = let rx = ref [] in fun x -> rx := [x]; rx := []; x;;
val id : '_weak1 -> '_weak1 = <fun>
我认为引用的这个属性不可能改变。
我认为你想要的是这个版本的id
在第一次实际使用时假设一个单态类型:
# id "yes";;
- : string = "yes"
# id;;
- : string -> string = <fun>
如果在实际代码中使用它,则需要在其模块结束之前获取具体类型。您不能将弱类型变量保留为undefined,否则会出现此错误:
Error: The type of this expression, '_weak1 -> '_weak1,
contains type variables that cannot be generalized
答案 1 :(得分:2)
另外两个答案基本上利用了只有值被推广的事实,因此如果你将你的定义包装在一个非值的东西中,它就不会被推广。因此,将它赋予id函数的技巧。
但是,如果您在帐户中采用宽松的价值限制,它就无法运作:
# let nil = id [] ;;
val nil : 'a list = []
因此,您需要确保所需的类型变量不会出现在协变位置。在你的第一个例子中,它出现在箭头的左边,所以没关系。否则,您需要通过隐藏类型定义并省略方差注释来确保其有效。
module M : sig
type 'a t
val make : 'a list -> 'a t
end = struct
type 'a t = 'a list
let make x = x
end
let x = M.make []
val x : '_weak1 M.t
答案 2 :(得分:1)
我同意Jeffrey Scofield's answer但是,在我看来,最好避免引用,没有它们就可以实现相同的行为:
# let id = let id = fun x -> x in id id;;
val id : '_weak1 -> '_weak1 = <fun>
之后,如果您需要使用其他签名的功能,例如eq : '_weak2 -> '_weak2 -> bool
,那么您只需要以正常方式实现eq
并将其传递给id
:< / p>
# let eq =
let id = let id = fun x -> x in id id in
let eq = (=) in (id (eq));;
val eq : '_weak2 -> '_weak2 -> bool = <fun>