Language agnostic 什么是存在主义类型?

Language agnostic 什么是存在主义类型?,language-agnostic,types,type-systems,existential-type,Language Agnostic,Types,Type Systems,Existential Type,我通读了维基百科的文章。我认为它们之所以被称为存在类型是因为存在运算符(∃). 不过,我不知道这有什么意义。两者之间有什么区别 T = ∃X { X a; int f(X); } 及 据我所知,这是描述接口/抽象类的数学方法 至于T=∃X{xa;intf(X);} 对于C#,它将转换为通用抽象类型: abstract class MyType<T>{ private T a; public abstract int f(T x); } 抽象类MyType{ 私人

我通读了维基百科的文章。我认为它们之所以被称为存在类型是因为存在运算符(∃). 不过,我不知道这有什么意义。两者之间有什么区别

T = ∃X { X a; int f(X); }


据我所知,这是描述接口/抽象类的数学方法

至于T=∃X{xa;intf(X);}

对于C#,它将转换为通用抽象类型:

abstract class MyType<T>{
    private T a;

    public abstract int f(T x);
}
抽象类MyType{
私人旅行社;
公开摘要int f(tx);
}

“存在主义”只是意味着有一些类型遵守此处定义的规则。

对抽象数据类型和信息隐藏的研究将存在类型引入编程语言。制作数据类型抽象隐藏了有关该类型的信息,因此该类型的客户端不能滥用它。假设您有一个对象的引用……某些语言您可以将该引用强制转换为对字节的引用,并对该内存块执行任何操作。为了保证程序的行为,语言可以强制您仅通过对象设计器提供的方法对该对象的引用进行操作。您知道该类型存在,但仅此而已

见:

抽象类型有存在类型MITCHEL&PLOTKIN


存在类型是不透明类型

想象一下Unix中的一个文件句柄。你知道它的类型是int,所以你可以很容易地伪造它。例如,你可以尝试从句柄43中读取。如果程序恰好有一个文件是用这个特定句柄打开的,你就可以从它中读取。你的代码不一定是恶意的,只是草率的(例如,句柄可能是一个未初始化的变量)

存在类型对您的程序是隐藏的。如果
fopen
返回了存在类型,您所能做的就是将其与接受此存在类型的某些库函数一起使用。例如,将编译以下伪代码:

let exfile=fopen(“foo.txt”);//exfile没有类型!
读取(exfile、buf、大小);
接口“read”声明为:

存在一个类型T,因此:

size\u t read(exfile,char*buf,size\u t size);

变量exfile不是int,不是char*,也不是结构文件。在类型系统中无法表达任何内容。不能声明类型未知的变量,也不能将指针强制转换为未知类型。当有人定义通用类型
∀X
他们说:你可以插入任何你想要的类型,我不需要知道任何关于类型的信息来完成我的工作,我只会把它隐晦地称为
X

当某人定义存在类型
∃X
他们在说:我将使用我想要的任何类型;您对该类型一无所知,因此只能将其不透明地称为
X

通用类型允许您编写以下内容:

void copy<T>(List<T> source, List<T> dest) {
   ...
}
interface VirtualMachine<B> {
   B compile(String source);
   void run(B bytecode);
}

// Now, if you had a list of VMs you wanted to run on the same input:
void runAllCompilers(List<∃B:VirtualMachine<B>> vms, String source) {
   for (∃B:VirtualMachine<B> vm : vms) {
      B bytecode = vm.compile(source);
      vm.run(bytecode);
   }
}
列表中的每个虚拟机实现都可以有不同的字节码类型。
runAllCompilers
函数不知道字节码类型是什么,但它不需要知道;它所做的只是将字节码从
VirtualMachine.compile
中继到
VirtualMachine.run

Java类型通配符(例如:
List
)是一种非常有限的存在类型

更新:忘记提到可以用通用类型模拟存在类型。首先,包装通用类型以隐藏类型参数。其次,反转控制(这有效地交换了上述定义中的“你”和“我”部分,这是存在类型和通用类型之间的主要区别)

VM实现示例:

class MyVM implements VirtualMachine<byte[]>, VMWrapper {
   public byte[] compile(String input) {
      return null; // TODO: somehow compile the input
   }
   public void run(byte[] bytecode) {
      // TODO: Somehow evaluate 'bytecode'
   }
   public void unwrap(VMHandler handler) {
      handler.handle(this);
   }
}
类MyVM实现虚拟机VMWrapper{ 公共字节[]编译(字符串输入){ return null;//TODO:以某种方式编译输入 } 公共无效运行(字节[]字节码){ //TODO:以某种方式计算“字节码” } 公共无效展开(VMHandler){ 处理(这个); } }
我认为将存在类型与通用类型一起解释是有意义的,因为这两个概念是互补的,即一个是另一个的“对立面”

我不能回答关于存在类型的每一个细节(比如给出确切的定义,列出所有可能的用法,它们与抽象数据类型的关系等等),因为我对此还不够了解。我将仅演示(使用Java)存在类型的主要效果是什么:

存在类型可以用于多种不同的用途。但它们所做的是在右侧“隐藏”类型变量。通常,出现在右侧的任何类型变量也必须出现在左侧[…]

示例设置:

===========================================================
                     |    universally       existentially
                     |  quantified type    quantified type
---------------------+-------------------------------------
 calling method      |                  
 needs to know       |        yes                no
 the type argument   |                 
---------------------+-------------------------------------
 called method       |                  
 can use / refer to  |        yes                no  
 the type argument   |                  
=====================+=====================================
下面的伪代码不是非常有效的Java,即使修复它很容易。事实上,这正是我在这个答案中要做的

类树
{
α值;
左树;
树权;
}
内部高度(树t)
{
返回值(t!=null)?1+最大值(高度(t左),高度(t右))
:  0;
}
让我简单地向你们解释一下。我们正在定义

  • 一种递归类型的
    ,表示二叉树中的一个节点。每个节点存储某种类型α的
    ,并引用同一类型的可选
    子树

  • 一个函数
    height
    ,它返回从任何叶节点到根节点的最远距离
    t

现在,让我们将上面的
height
伪代码转换成正确的Java语法!(为了简洁起见,我将继续省略一些样板文件,例如面向对象和可访问性修饰符。)我将展示两种可能的解决方案

1.通用型解决方案:void runWithAll(List<VMWrapper> vms, final String input) { for (VMWrapper vm : vms) { vm.unwrap(new VMHandler() { public <B> void handle(VirtualMachine<B> vm) { B bytecode = vm.compile(input); vm.run(bytecode); } }); } }
class MyVM implements VirtualMachine<byte[]>, VMWrapper {
   public byte[] compile(String input) {
      return null; // TODO: somehow compile the input
   }
   public void run(byte[] bytecode) {
      // TODO: Somehow evaluate 'bytecode'
   }
   public void unwrap(VMHandler handler) {
      handler.handle(this);
   }
}
===========================================================
                     |    universally       existentially
                     |  quantified type    quantified type
---------------------+-------------------------------------
 calling method      |                  
 needs to know       |        yes                no
 the type argument   |                 
---------------------+-------------------------------------
 called method       |                  
 can use / refer to  |        yes                no  
 the type argument   |                  
=====================+=====================================
T = ∃X { X a; int f(X); }
T = int
T = ∀X { X a; int f(X); }
(∃b. F(b)) -> Int
∀b. (F(b) -> Int)
public class MyClass<T> {
   // T is existential in here
   T whatever; 
   public MyClass(T w) { this.whatever = w; }

   public static MyClass<?> secretMessage() { return new MyClass("bazzlebleeb"); }
}

// T is universal from out here
MyClass<String> mc1 = new MyClass("foo");
MyClass<Integer> mc2 = new MyClass(123);
MyClass<?> mc3 = MyClass.secretMessage();
public class ToDraw<T> {
    T obj;
    Function<Pair<T,Graphics>, Void> draw;
    ToDraw(T obj, Function<Pair<T,Graphics>, Void>
    static void draw(ToDraw<?> d, Graphics g) { d.draw.apply(new Pair(d.obj, g)); }
}

// Now you can put these in a list and draw them like so:
List<ToDraw<?>> drawList = ... ;
for(td in drawList) ToDraw.draw(td);
trait Existential {
  type Parameter <: Interface
}
trait Existential[Parameter <: Interface]