OCaml坚持认为函数不是多态的,但不要指定类型

时间:2018-05-22 04:57:43

标签: ocaml

是否有可能明确写下非多态但延迟统一的类型,如下划线类型?

因此,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

并且不依赖于可能在未来可能会消失的类型系统的限制。

3 个答案:

答案 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>