Language agnostic 编码与将存在类型编码为通用类型时使用存在类型

Language agnostic 编码与将存在类型编码为通用类型时使用存在类型,language-agnostic,ocaml,type-systems,existential-type,quantifiers,Language Agnostic,Ocaml,Type Systems,Existential Type,Quantifiers,在我们将存在类型转换为一个通用类型之后,我试图更好地理解编码与使用存在类型之间的细微差别。简而言之,在我看来,使用存在主义类型比编码存在主义类型容易得多,下面我将解释这对我意味着什么。为了更好地解释这一点,让我们从逻辑的两条规则开始 ∀x、 P(x)⇒ ¬(∃x、 P(x)) ∃x、 P(x)⇒ ¬(∀x、 P(x)) 从这个,我们有那个 val int_package : (int mypackage -> '_a) -> '_a = <fun> val str_pac

在我们将存在类型转换为一个通用类型之后,我试图更好地理解编码与使用存在类型之间的细微差别。简而言之,在我看来,使用存在主义类型比编码存在主义类型容易得多,下面我将解释这对我意味着什么。为了更好地解释这一点,让我们从逻辑的两条规则开始

  • ∀x、 P(x)⇒ ¬(∃x、 P(x))
  • ∃x、 P(x)⇒ ¬(∀x、 P(x))
  • 从这个,我们有那个

    val int_package : (int mypackage -> '_a) -> '_a = <fun>
    val str_package : (string mypackage -> '_a) -> '_a = <fun>
    
  • ∀x、 (P(x)⇒ Q)
  • ∀x、 (-P(x)⩖ Q)
  • (∀x、 P(x))⩖ Q
  • ((∀x、 P(x)))⩖ Q
  • (、)(∀x、 P(x)))⩖ Q
  • ((∃x、 P(x)))⩖ Q
  • (∃x、 P(x))⇒ Q
  • 因此,

    (∃x、 P(x))⇒ Q=∀x、 (P(x)⇒ Q)

    换句话说,如果函数的第一个参数是存在的,我们可以将存在类型拉到左边,并将其表示为一个通用类型。这就是我所说的存在主义规则的使用。现在,通常当人们谈论普遍存在类型和存在类型之间的等价性时,我们会看到

    ∃x、 P(x)=∀y(∀x、 P(x)⇒ y)⇒ y

    这就是我所说的存在规则的编码。为了看到这种等价性,我们有

  • ∀y(∀x、 P(x)⇒ y)⇒ y
  • ∀y((∃x、 P(x))⇒ y)⇒ y
  • ∀y、 ,((∃x、 P(x))⇒ y)⩖ y
  • ∀y、 ()(∃x、 P(x))⩖ y)⩖ y
  • ∀y((∃x、 P(x))⩕ (y)⩖ y
  • ∀y((∃x、 P(x))⩖ y)⩕ (y-v-y)
  • ∀y(∃x、 P(x))⩖ y
  • (∃x、 P(x))⩖ ∀y、 y
  • ∃x、 P(x)
  • 好的,这个规则告诉我,存在类型是一个函数,它接受一个程序,将p(x)转换成y,然后输出y

    现在,这里是我关于使用存在主义和构建存在主义之间的区别的意思。假设我们想用像OCaml这样的语言构建一个穷人的模块,我们可以用这样的程序来实现

    type 't mypackage = {
        add : 't * 't -> 't;
        fromInt : int -> 't;
        toString : 't -> string};;
    
    let int_package  = {
        add  = (fun (x,y) -> x+y) ;
        fromInt = (fun x->x) ;
        toString = (fun x->string_of_int x)};;
    
    let str_package  = {
        add  = (fun (x,y) -> x^y) ;
        fromInt = (fun x->string_of_int x) ;
        toString = (fun x-> x)};;
    
    let simpleProg fns =
       let exp01 = fns.fromInt 1 in
       let exp02 = fns.fromInt 2 in
       let exp03 = fns.add (exp01,exp02) in
       fns.toString exp03
    
    let _ = simpleProg int_package;;
    let _ = simpleProg str_package;;
    
    这使用了上面的存在规则。就类型而言,我们有

    val int_package : int mypackage
    val str_package : string mypackage
    val simpleProg : 'a mypackage -> string = <fun>
    
    这里,我们有

    val int_package : (int mypackage -> '_a) -> '_a = <fun>
    val str_package : (string mypackage -> '_a) -> '_a = <fun>
    
    我想我在这个过程中学到的最大的一点就是,这种重新编码的技巧颠倒了事物的构造顺序。本质上,这些包建立了一个过程,在这个过程中,它们接受一个程序,然后将这个程序应用于它们的内部表示。通过使用此技巧,包的内部类型被隐藏。虽然这在理论上等同于存在主义类型,但就我个人而言,我发现这个过程不同于皮尔斯的《类型和编程语言》一书中所描述的存在主义类型的直接实现

    直接回答我上面的问题

  • 如果您只对将包直接传递给函数感兴趣,那么通用包技巧可以工作,并且更容易实现。它绝对不是一个存在主义软件包,但它的用法与在相同上下文中使用的真正存在主义软件包非常相似,但没有解包。通过类似,我的意思是我们保留了将基于不同类型的包的不同表示形式传递到函数中,然后使用这些表示形式进行一般计算的能力
  • 我们在这些通用软件包中失去的是将这些软件包视为真正的一流成员的能力。最简单的例子是,我们不能将这些通用包放入列表中,因为它们的类型是公开的。真正的存在包隐藏了类型,因此更容易传递给更大的程序

  • 此外,通用软件包是一个可怕的词。int_包和str_包是专用的,所以它们不是真正通用的。大多数情况下,我没有更好的名字。

    坦率地说,这有点让我不知所措,但我想我知道你的基本想法。我记得,Haskell中也有类似的东西支持存在类型

    但是,第二个构造的类型在我看来并不是很有用:

    val int_package : (int mypackage -> '_a) -> '_a = <fun>
    
    val int\u package:(int mypackage->'\u a)->'\u a=
    
    这是一种单态类型,其中
    \u a
    尚未指定。它不是多态类型。这意味着您只能给它一种类型的程序。如果您编写的第二个程序希望返回int而不是字符串,那么您的存在性包将不允许您调用它。至少在我看来是这样的

    第一个构造有一个真正的多态类型,因此它看起来应该工作得更好


    (一个知识更渊博的类型理论类型的人可能会提供更多的帮助:-)

    我真的不理解你的问题,但你对存在主义的编码似乎不正确

    正如你所提到的,如果你想模仿
    ∃'t、 "t mypackage
    ,则必须创建一个类型

    ∀'y. (∀'t. 't mypackage -> 'y) -> 'y
    
    但这不是OCaml类型
    ('t mypackage->'y)->'y
    ,更准确地说

    ∀'y. ∀'t. ('t mypackage -> 'y) -> 'y
    
    看量词的位置

    OCaml的类型方案是最量化的,它不能有像
    ∀'Y(∀'t、 't mypackage->'y)->'y
    ,但我们可以用其记录多态字段来模拟它:

    type 'y packed = { unpacked : 't. 't mypackage -> 'y }  
    (* mimicing ∀'t. 't mypackage -> 'y *)
    
    使用此类型,存在类型可以实现为

    type 'y closed_package = 'y packed -> 'y 
    (* mimicing a higher ranked type ∀'y. (∀'t. 't mypackage -> 'y) -> 'y,
       which is equivalent with ∃'t. 't mypackage *)
    
    如果您不喜欢暴露类型变量
    'y
    ,可以使用记录多态字段再次隐藏它:

    type really_closed_package = { l : 'y. 'y closed_package }
    
    包实现可以打包到此接口中,如下所示:

    let closed_int_package = { l = fun packed -> packed.unpacked int_package }
    let closed_str_package = { l = fun packed -> packed.unpacked str_package }
    
    由于这些打包版本具有相同的类型,我们可以将它们放入列表中:

    let closed_packages = [ closed_int_package; closed_str_package ]
    
    这通常是我们想要对存在主义做的

    现在编码完成了。使用它也需要一些复杂性,但非常简单:

    let doubled_int_string p x =
      let x = p.fromInt x in
      p.toString (p.add (x,x))
    
    double\u int\u string
    用于打开的包,我们不能简单地将其用于关闭的包。我们需要一些转换:

    let () =
      (* Using "universal" packages *)
      print_endline (double_int_string int_package 3);
      print_endline (double_int_string str_package 3);
    
      (* Equivalents using "existential" packages *)
      print_endline (closed_int_package.l { unpacked = doubled_int_string } 3);
      print_endline (closed_str_package.l { unpacked = doubled_int_string } 3)
    

    正如camlspotter所指出的,您的编码不太正确,应该使用以下类型:

    type 'k mypackage_cont = { p: 't. 't mypackage -> 'k } 
    
    那么您的编码包将具有以下类型:

    val int_package1 : 'k mypackage_cont -> 'k
    val str_package1 : 'k mypackage_cont -> 'k
    
    而你的另一个版本
    let () =
      (* Using "universal" packages *)
      print_endline (double_int_string int_package 3);
      print_endline (double_int_string str_package 3);
    
      (* Equivalents using "existential" packages *)
      print_endline (closed_int_package.l { unpacked = doubled_int_string } 3);
      print_endline (closed_str_package.l { unpacked = doubled_int_string } 3)
    
    type 'k mypackage_cont = { p: 't. 't mypackage -> 'k } 
    
    val int_package1 : 'k mypackage_cont -> 'k
    val str_package1 : 'k mypackage_cont -> 'k
    
    val int_package2 : int mypackage
    val str_package2 : string mypackage
    
    # [ int_package1; str_package1; ];;
    - : ('a mypackage_cont -> 'a) list = [<fun>; <fun>]
    
    # [ int_package2; str_package2 ];;
    Characters 16-28:
      [ int_package2; str_package2 ];;
                      ^^^^^^^^^^^^
    Error: This expression has type string mypackage
           but an expression was expected of type int mypackage
           Type string is not compatible with type int