代码之家  ›  专栏  ›  技术社区  ›  Greg Nisbet

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

  •  2
  • Greg Nisbet  · 技术社区  · 6 年前

    有没有可能显式地写下一个非多态的类型,但却像下划线类型一样延迟了统一?

    因此,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 回复  |  直到 6 年前
        1
  •  2
  •   Jeffrey Scofield    6 年前

    可以使用引用不可归纳的事实。

    # 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>
    

    如果在真正的代码中使用它,它将需要在其模块结束之前获得一个具体的类型。不能不定义弱类型变量,否则会出现以下错误:

    Error: The type of this expression, '_weak1 -> '_weak1,
           contains type variables that cannot be generalized
    
        2
  •  2
  •   Drup    6 年前

    另外两个答案基本上利用了这样一个事实 价值观 是广义的,所以如果你把你的定义包装成一个非值的东西,它就不会被广义化。因此,把它赋给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
    
        3
  •  1
  •   vonaka    6 年前

    我同意 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 :

    # let eq =
        let id = let id = fun x -> x in id id in
        let eq = (=) in (id (eq));;
    val eq : '_weak2 -> '_weak2 -> bool = <fun>