Một loại tồn tại là gì?


171

Tôi đọc qua bài viết Wikipedia Các loại hiện sinh . Tôi tập hợp rằng chúng được gọi là các kiểu tồn tại vì toán tử tồn tại (). Tôi không chắc ý của nó là gì. Sự khác biệt giữa

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

T = ∀x { X a; int f(X); }

?


8
Đây có thể là một chủ đề tốt cho lập trình viên.stackexchange.com. lập trình
viên.stackexchange.com

Câu trả lời:


192

Khi ai đó định nghĩa một loại phổ quát ∀Xmà họ đang nói: Bạn có thể cắm bất kỳ loại nào bạn muốn, tôi không cần biết bất cứ điều gì về loại công việc của mình, tôi sẽ chỉ đề cập đến nó một cách ngẫu nhiên nhưX .

Khi ai đó định nghĩa một loại tồn tại ∃Xhọ đang nói: Tôi sẽ sử dụng bất kỳ loại nào tôi muốn ở đây; bạn sẽ không biết gì về loại này, vì vậy bạn chỉ có thể tham khảo nó một cách rõ ràng nhưX .

Các loại phổ quát cho phép bạn viết những thứ như:

void copy<T>(List<T> source, List<T> dest) {
   ...
}

Các copychức năng không có ý tưởng những gì Tthực sự sẽ được, nhưng nó không cần.

Các kiểu hiện sinh sẽ cho phép bạn viết những thứ như:

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);
   }
}

Mỗi triển khai máy ảo trong danh sách có thể có một loại mã byte khác nhau. Các runAllCompilerschức năng không có ý tưởng gì loại bytecode là, nhưng nó không cần phải; tất cả những gì nó làm là chuyển tiếp mã byte từ VirtualMachine.compileđến VirtualMachine.run.

Các ký tự đại diện kiểu Java (ví dụ List<?>:) là một dạng rất hạn chế của các kiểu tồn tại.

Cập nhật: Quên đề cập rằng bạn có thể sắp xếp mô phỏng các loại tồn tại với các loại phổ quát. Đầu tiên, bọc loại phổ quát của bạn để ẩn tham số loại. Thứ hai, điều khiển đảo ngược (điều này có hiệu quả hoán đổi phần "bạn" và "tôi" trong các định nghĩa ở trên, đây là điểm khác biệt chính giữa các tồn tại và phổ quát).

// A wrapper that hides the type parameter 'B'
interface VMWrapper {
   void unwrap(VMHandler handler);
}

// A callback (control inversion)
interface VMHandler {
   <B> void handle(VirtualMachine<B> vm);
}

Bây giờ chúng ta có thể có VMWrappercuộc gọi của riêng chúng ta VMHandlerhandlechức năng đánh máy phổ quát . Hiệu ứng ròng là như nhau, mã của chúng tôi phải coi Blà mờ đục.

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);
         }
      });
   }
}

Một ví dụ về triển khai 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);
   }
}

12
@Kannan, +1 cho câu trả lời rất hữu ích, nhưng hơi khó nắm bắt: 1. Tôi nghĩ sẽ hữu ích nếu bạn có thể rõ ràng hơn về bản chất kép của các loại hiện sinh và phổ quát. Tôi chỉ tình cờ nhận ra cách bạn diễn đạt hai đoạn đầu rất giống nhau; chỉ sau này bạn mới nói rõ rằng cả hai định nghĩa về cơ bản là giống nhau, nhưng với "tôi" và "bạn" đã đảo ngược. Ngoài ra, tôi đã không hiểu ngay lập tức "tôi" và "bạn" được đề cập đến.
stakx - không còn đóng góp

2
(tiếp tục :) 2. Tôi không hiểu đầy đủ ý nghĩa của ký hiệu toán học trong List<∃B:VirtualMachine<B>> vmshoặc for (∃B:VirtualMachine<B> vm : vms). (Vì đây là các loại chung, bạn không thể sử dụng các ?ký tự đại diện của Java thay vì cú pháp "tự tạo"?) Tôi nghĩ rằng có thể có một ví dụ mã trong đó không có các loại chung chung như ∃B:VirtualMachine<B>có liên quan, mà thay vào đó là "thẳng" ∃B, bởi vì các loại chung dễ dàng được liên kết với các loại phổ quát sau các ví dụ mã đầu tiên của bạn.
stakx - không còn đóng góp

2
Tôi đã từng ∃Brõ ràng về nơi định lượng đang xảy ra. Với cú pháp ký tự đại diện, bộ định lượng được ngụ ý ( List<List<?>>thực tế có nghĩa là ∃T:List<List<T>>không List<∃T:List<T>>). Ngoài ra, định lượng rõ ràng cho phép bạn tham khảo loại (tôi đã sửa đổi ví dụ để tận dụng lợi thế này bằng cách lưu mã byte của loại Btrong một biến tạm thời).
Kannan Goundan

2
Ký hiệu toán học được sử dụng ở đây cẩu thả như địa ngục, nhưng tôi không nghĩ đó là lỗi của người trả lời (đó là tiêu chuẩn). Tuy nhiên, tốt nhất không nên lạm dụng các bộ lượng hóa hiện sinh và phổ quát theo cách như vậy có lẽ ...
Noldorin

2
@Kannan_Goundan, tôi muốn biết điều gì khiến bạn nói các ký tự đại diện Java là phiên bản rất giới hạn của điều này. Bạn có biết rằng bạn có thể triển khai hàm ví dụ runAllCompilers đầu tiên của mình trong Java thuần túy (với hàm trợ giúp để truy xuất (đặt tên cho) tham số wilcard) không?
LP_

107

Một giá trị của một kiểu tồn tại như ∃x. F(x) là một cặp chứa một số loại x và một giá trị của loại F(x). Trong khi đó một giá trị của một loại đa hình như ∀x. F(x)là một hàm lấy một số loại xtạo ra một giá trị của loại F(x). Trong cả hai trường hợp, kiểu đóng trên một số hàm tạo F.

Lưu ý rằng chế độ xem này trộn lẫn các loại và giá trị. Bằng chứng tồn tại là một loại và một giá trị. Bằng chứng phổ quát là toàn bộ họ các giá trị được lập chỉ mục theo loại (hoặc ánh xạ từ loại sang giá trị).

Vì vậy, sự khác biệt giữa hai loại bạn đã chỉ định như sau:

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

Điều này có nghĩa là: Một giá trị của loại Tchứa một loại được gọi X, một giá trị a:Xvà một hàm f:X->int. Nhà sản xuất các giá trị của loại Tđược chọn bất kỳ loại nàoX và người tiêu dùng không thể biết gì về nó X. Ngoại trừ việc có một ví dụ về nó được gọi avà giá trị này có thể được biến thành một intbằng cách đưa nó vào f. Nói cách khác, một giá trị của loại Tbiết cách tạo ra một intcách nào đó. Vâng, chúng tôi có thể loại bỏ các loại trung gian Xvà chỉ cần nói:

T = int

Một số lượng phổ quát là một chút khác nhau.

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

Điều này có nghĩa là: Một giá trị của loại Tcó thể được cung cấp cho bất kỳ loại nào Xvà nó sẽ tạo ra một giá trị a:Xvà một hàm f:X->int bất kể đó Xlà gì . Nói cách khác: một người tiêu dùng các giá trị của loại Tcó thể chọn bất kỳ loại nào cho X. Và một nhà sản xuất các giá trị loại Tkhông thể biết bất cứ điều gì về Xnó, nhưng nó phải có khả năng tạo ra một giá trị acho bất kỳ lựa chọn nào Xvà có thể biến giá trị đó thành một int.

Rõ ràng việc thực hiện loại này là không thể, bởi vì không có chương trình nào có thể tạo ra giá trị của mọi loại có thể tưởng tượng được. Trừ khi bạn cho phép những điều vô lý như nullhoặc đáy.

Vì một tồn tại là một cặp, nên một đối số hiện sinh có thể được chuyển đổi thành một đối số phổ biến thông qua cà ri .

(∃b. F(b)) -> Int

giống như:

∀b. (F(b) -> Int)

Các cựu là một tồn tại hạng 2 . Điều này dẫn đến các thuộc tính hữu ích sau:

Mỗi loại thứ hạng n+1được định lượng tồn tại là một loại thứ hạng được định lượng toàn cầu n.

Có một thuật toán tiêu chuẩn để biến các tồn tại thành vũ trụ, được gọi là Skolemization .


7
Có thể hữu ích (hoặc không) khi đề cập đến Skolemization en.wikipedia.org/wiki/Skolem_n
Geoff Reedy

34

Tôi nghĩ sẽ hợp lý khi giải thích các loại tồn tại cùng với các loại phổ quát, vì hai khái niệm này là bổ sung cho nhau, tức là một khái niệm "đối lập" với loại kia.

Tôi không thể trả lời từng chi tiết về các kiểu tồn tại (chẳng hạn như đưa ra một định nghĩa chính xác, liệt kê tất cả các cách sử dụng có thể, mối quan hệ của chúng với các loại dữ liệu trừu tượng, v.v.) bởi vì tôi đơn giản là không đủ hiểu biết về điều đó. Tôi sẽ chỉ trình bày (sử dụng Java) những gì bài viết HaskellWiki này nói là tác dụng chính của các loại tồn tại:

Các loại tồn tại có thể được sử dụng cho một số mục đích khác nhau. Nhưng những gì họ làm là 'ẩn' một biến loại ở phía bên tay phải. Thông thường, bất kỳ biến loại nào xuất hiện ở bên phải cũng phải xuất hiện ở bên trái [Rời]

Thiết lập ví dụ:

Mã giả sau đây không hoàn toàn hợp lệ với Java, mặc dù nó có thể đủ dễ để sửa nó. Trên thực tế, đó chính xác là những gì tôi sẽ làm trong câu trả lời này!

class Tree<α>
{
    α       value;
    Tree<α> left;
    Tree<α> right;
}

int height(Tree<α> t)
{
    return (t != null)  ?  1 + max( height(t.left), height(t.right) )
                        :  0;
}

Hãy để tôi đánh vần ngắn gọn điều này cho bạn. Chúng tôi đang xác định

  • một kiểu đệ quy Tree<α>đại diện cho một nút trong cây nhị phân. Mỗi nút lưu trữ một valuesố loại α và có các tham chiếu đến các tùy chọn leftvà các rightcây con cùng loại.

  • một hàm heighttrả về khoảng cách xa nhất từ ​​bất kỳ nút lá nào đến nút gốc t.

Bây giờ, hãy biến mã giả ở trên heightthành cú pháp Java thích hợp! (Tôi sẽ tiếp tục bỏ qua một số bản tóm tắt vì lý do ngắn gọn, chẳng hạn như định hướng đối tượng và sửa đổi khả năng truy cập.) Tôi sẽ trình bày hai giải pháp khả thi.

1. Giải pháp loại phổ quát:

Cách khắc phục rõ ràng nhất là chỉ đơn giản là tạo heightchung bằng cách đưa tham số loại α vào chữ ký của nó:

<α> int height(Tree<α> t)
{
    return (t != null)  ?  1 + max( height(t.left), height(t.right) )
                        :  0;
}

Điều này sẽ cho phép bạn khai báo các biến và tạo các biểu thức kiểu α bên trong hàm đó, nếu bạn muốn. Nhưng...

2. Giải pháp loại hiện sinh:

Nếu bạn nhìn vào cơ thể của phương thức của chúng tôi, bạn sẽ nhận thấy rằng chúng tôi không thực sự truy cập hoặc làm việc với bất kỳ thứ gì thuộc loại α ! Không có biểu thức nào có kiểu đó, cũng không có biến nào được khai báo với kiểu đó ... vậy, tại sao chúng ta phải tạo ra heightchung chung? Tại sao chúng ta không thể quên đi α ? Hóa ra, chúng ta có thể:

int height(Tree<?> t)
{
    return (t != null)  ?  1 + max( height(t.left), height(t.right) )
                        :  0;
}

Như tôi đã viết ở phần đầu của câu trả lời này, các kiểu tồn tại và phổ quát là bổ sung / kép về bản chất. Do đó, nếu giải pháp loại phổ quát là tạo ra chung chung height hơn , thì chúng ta nên kỳ vọng rằng các loại tồn tại có tác dụng ngược lại: làm cho nó ít chung chung hơn , cụ thể bằng cách ẩn / xóa tham số loại α .

Kết quả là, bạn không còn có thể tham khảo loại t.valuetrong phương thức này cũng như không thao tác bất kỳ biểu thức nào của loại đó, bởi vì không có định danh nào bị ràng buộc với nó. (Ký ?tự đại diện là một mã thông báo đặc biệt, không phải là mã định danh "bắt giữ" một loại.) t.valueĐã thực sự trở nên mờ đục; có lẽ điều duy nhất bạn vẫn có thể làm với nó là loại nó Object.

Tóm lược:

===========================================================
                     |    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   |                  
=====================+=====================================

3
Lời giải thích hay. Bạn không cần truyền t.value cho Object, bạn chỉ có thể gọi nó là Object. Tôi muốn nói rằng kiểu tồn tại làm cho phương thức chung chung hơn vì điều đó. Điều duy nhất bạn có thể biết về t.value là đó là một Đối tượng trong khi bạn có thể nói điều gì đó cụ thể về α (như α kéo dài Nối tiếp).
Craig P. Motlin

1
Trong khi đó, tôi đã tin rằng câu trả lời của tôi không thực sự giải thích được sự tồn tại là gì và tôi đang xem xét viết một câu khác giống như hai đoạn đầu của câu trả lời của Kannan Goudan, mà tôi nghĩ là gần với "sự thật" hơn. Điều đó đang được nói, @Craig: So sánh khái quát với Objectkhá thú vị: Mặc dù cả hai đều giống nhau ở chỗ chúng cho phép bạn viết mã độc lập kiểu tĩnh, nhưng trước đây (generic) không bỏ đi gần như tất cả các thông tin loại có sẵn đạt được mục tiêu này Theo nghĩa đặc biệt này, thuốc generic là một phương thuốc cho ObjectIMO.
stakx - không còn đóng góp

1
@stakx, trong mã này (từ Java hiệu quả) public static void swap(List<?> list, int i, int j) { swapHelper(list, i, j); } private static <E> void swapHelper(List<E> list, int i, int j) { list.set(i, list.set(j, list.get(i))); } , Elà một universal type?đại diện cho một existential type?
Kevin Meredith

Câu trả lời này không đúng. Kiểu ?trong int height(Tree<?> t)vẫn chưa được biết bên trong hàm và vẫn được xác định bởi người gọi bởi vì đó là người gọi phải chọn cây nào để truyền vào. Ngay cả khi mọi người gọi đây là kiểu tồn tại trong Java, thì không. Trình ?giữ chỗ có thể được sử dụng để triển khai một dạng tồn tại trong Java, trong một số trường hợp, nhưng đây không phải là một trong số chúng.
Peter Hall

15

Đây là tất cả các ví dụ tốt, nhưng tôi chọn trả lời nó một chút khác nhau. Nhớ lại từ toán học, đó x. P (x) có nghĩa là "với mọi x, tôi có thể chứng minh rằng P (x)". Nói cách khác, nó là một loại hàm, bạn cho tôi một x và tôi có một phương pháp để chứng minh nó cho bạn.

Trong lý thuyết loại, chúng ta không nói về bằng chứng, mà là về loại. Vì vậy, trong không gian này, chúng tôi có nghĩa là "đối với bất kỳ loại X nào bạn cung cấp cho tôi, tôi sẽ cung cấp cho bạn một loại P cụ thể". Bây giờ, vì chúng tôi không cung cấp cho P nhiều thông tin về X bên cạnh thực tế rằng đó là một loại, P không thể làm gì nhiều với nó, nhưng có một số ví dụ. P có thể tạo loại "tất cả các cặp cùng loại" : P<X> = Pair<X, X> = (X, X). Hoặc chúng ta có thể tạo loại tùy chọn : P<X> = Option<X> = X | Nil, trong đó Nil là loại con trỏ null. Chúng ta có thể tạo một danh sách từ nó : List<X> = (X, List<X>) | Nil. Lưu ý rằng cái cuối cùng là đệ quy, các giá trị của List<X>một trong hai cặp trong đó phần tử đầu tiên là X và phần tử thứ hai là mộtList<X> hoặc nếu không nó là một con trỏ null.

Bây giờ, trong toán ∃x. P (x) có nghĩa là "Tôi có thể chứng minh rằng có một x cụ thể sao cho P (x) là đúng". Có thể có nhiều x như vậy, nhưng để chứng minh, một là đủ. Một cách khác để nghĩ về nó là phải tồn tại một tập hợp các cặp bằng chứng và bằng chứng không trống rỗng {(x, P (x))}.

Dịch theo lý thuyết loại: Một loại trong gia đình ∃X.P<X>là loại X và loại tương ứng P<X>. Lưu ý rằng trước khi chúng tôi đưa X cho P, (để chúng tôi biết mọi thứ về X nhưng P rất ít) thì điều ngược lại là đúng. P<X>không hứa sẽ cung cấp bất kỳ thông tin nào về X, chỉ là có một thông tin và đó thực sự là một loại.

Điều này hữu ích như thế nào? Chà, P có thể là một loại có cách phơi bày loại bên trong X. Một ví dụ sẽ là một đối tượng che giấu biểu diễn bên trong của trạng thái X. Mặc dù chúng ta không có cách nào trực tiếp thao tác với nó, chúng ta có thể quan sát hiệu ứng của nó bằng cách chọc vào P. Có thể có nhiều triển khai kiểu này, nhưng bạn có thể sử dụng tất cả các loại này cho dù loại nào được chọn.


2
Hmm, nhưng chức năng đạt được gì khi biết nó là một P<X>thay vì P(cùng chức năng và loại thùng chứa, giả sử, nhưng bạn không biết nó chứa X)?
Claudiu

3
Nói đúng ra, ∀x. P(x)không có nghĩa gì về khả năng chứng minh P(x), chỉ có sự thật.
R .. GitHub DỪNG GIÚP ICE

11

Để trả lời trực tiếp câu hỏi của bạn:

Với loại phổ quát, việc sử dụng Tphải bao gồm tham số loại X. Ví dụ T<String>hay T<Integer>. Đối với kiểu sử dụng Thiện tại, không bao gồm tham số loại đó vì nó không xác định hoặc không liên quan - chỉ sử dụng T(hoặc trong Java T<?>).

Thêm thông tin:

Các loại phổ quát / trừu tượng và các loại tồn tại là một tính hai mặt của phối cảnh giữa người tiêu dùng / khách hàng của một đối tượng / chức năng và nhà sản xuất / thực hiện nó. Khi một bên nhìn thấy một loại phổ quát thì bên kia nhìn thấy một loại tồn tại.

Trong Java, bạn có thể định nghĩa một lớp chung:

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();
  • Từ góc nhìn của một khách hàng của MyClass, Tlà phổ quát vì bạn có thể thay thế bất kỳ loại choT khi bạn sử dụng lớp đó và bạn phải biết loại thực tế của T bất cứ khi nào bạn sử dụng một thể hiện củaMyClass
  • Từ quan điểm của các phương thức cá thể MyClass,T tồn tại bởi vì nó không biết loại thực sự củaT
  • Trong Java, ?đại diện cho kiểu tồn tại - do đó, khi bạn ở trong lớp, Tvề cơ bản ?. Nếu bạn muốn xử lý một thể hiện MyClassvới tính Ttồn tại, bạn có thể khai báo MyClass<?>như trong secretMessage()ví dụ trên.

Các kiểu hiện sinh đôi khi được sử dụng để ẩn các chi tiết thực hiện của một cái gì đó, như được thảo luận ở nơi khác. Một phiên bản Java của cái này có thể trông giống như:

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);

Có một chút khó khăn để nắm bắt điều này một cách chính xác bởi vì tôi đang giả vờ ở một loại ngôn ngữ lập trình chức năng nào đó, mà Java thì không. Nhưng vấn đề ở đây là bạn đang nắm bắt một số loại trạng thái cộng với một danh sách các hàm hoạt động ở trạng thái đó và bạn không biết loại thực sự của phần trạng thái, nhưng các hàm đã làm vì chúng đã khớp với loại đó rồi .

Bây giờ, trong Java tất cả các loại không nguyên thủy không phải là nguyên thủy đều tồn tại một phần. Điều này nghe có vẻ lạ, nhưng vì một biến được khai báo Objectcó khả năng là một lớp con Objectthay vào đó, bạn không thể khai báo loại cụ thể, chỉ "loại này hoặc một lớp con". Và do đó, các đối tượng được biểu diễn dưới dạng một chút trạng thái cộng với một danh sách các hàm hoạt động trên trạng thái đó - chính xác là hàm nào được gọi được xác định khi chạy bằng cách tra cứu. Điều này rất giống với việc sử dụng các loại tồn tại ở trên nơi bạn có một phần trạng thái tồn tại và một chức năng hoạt động trên trạng thái đó.

Trong các ngôn ngữ lập trình được gõ tĩnh mà không có phân nhóm và phôi, các kiểu tồn tại cho phép một người quản lý danh sách các đối tượng được gõ khác nhau. Một danh sách T<Int>không thể chứa a T<Long>. Tuy nhiên, một danh sách T<?>có thể chứa bất kỳ biến thể nào T, cho phép một người đưa nhiều loại dữ liệu khác nhau vào danh sách và chuyển đổi tất cả thành int (hoặc thực hiện bất kỳ thao tác nào được cung cấp bên trong cấu trúc dữ liệu) theo yêu cầu.

Người ta có thể luôn luôn chuyển đổi một bản ghi với một loại tồn tại thành một bản ghi mà không cần sử dụng các bao đóng. Một đóng cũng tồn tại được gõ, trong đó các biến miễn phí mà nó được đóng lại được ẩn khỏi người gọi. Do đó, một ngôn ngữ hỗ trợ các kiểu đóng nhưng không phải kiểu tồn tại có thể cho phép bạn tạo các bao đóng có cùng trạng thái ẩn mà bạn sẽ đặt vào phần tồn tại của một đối tượng.


11

Một loại tồn tại là một loại mờ.

Hãy nghĩ về một tệp xử lý trong Unix. Bạn biết loại của nó là int, vì vậy bạn có thể dễ dàng giả mạo nó. Ví dụ, bạn có thể thử đọc từ tay cầm 43. Nếu điều đó xảy ra là chương trình có một tệp được mở bằng tay cầm cụ thể này, bạn sẽ đọc từ nó. Mã của bạn không phải là độc hại, chỉ là cẩu thả (ví dụ, tay cầm có thể là một biến chưa được khởi tạo).

Một kiểu tồn tại được ẩn khỏi chương trình của bạn. Nếu fopentrả về một kiểu tồn tại, tất cả những gì bạn có thể làm với nó là sử dụng nó với một số hàm thư viện chấp nhận kiểu tồn tại này. Ví dụ, mã giả sau đây sẽ biên dịch:

let exfile = fopen("foo.txt"); // No type for exfile!
read(exfile, buf, size);

Giao diện "đọc" được khai báo là:

Tồn tại một loại T sao cho:

size_t read(T exfile, char* buf, size_t size);

Biến exfile không phải là int, không phải a char*, không phải là tệp File struct, không có gì bạn có thể diễn tả trong hệ thống kiểu. Bạn không thể khai báo một biến có loại không xác định và bạn không thể sử dụng một con trỏ vào loại không xác định đó. Ngôn ngữ sẽ không cho phép bạn.


9
Điều này sẽ không làm việc. Nếu chữ ký của read∃T.read(T file, ...)sau đó không có gì là bạn có thể vượt qua trong tham số đầu tiên. Điều gì sẽ làm việc là fopentrả về tay cầm tập tin chức năng đọc được phân chia theo cùng một tồn tại :∃T.(T, read(T file, ...))
Kannan Goundan

2
Điều này dường như chỉ nói về ADT.
kizzx2

7

Có vẻ như tôi đến hơi muộn, nhưng dù sao, tài liệu này bổ sung một cái nhìn khác về các loại tồn tại, mặc dù không cụ thể là ngôn ngữ, nhưng nó sẽ dễ hiểu hơn về các loại tồn tại: http: //www.cs.uu .nl / nhóm / ST / Dự án / ehc / ehc-book.pdf (chương 8)

Sự khác biệt giữa một loại định lượng phổ biến và tồn tại có thể được đặc trưng bởi quan sát sau đây:

  • Việc sử dụng một giá trị với loại được định lượng determines xác định loại để chọn cho việc khởi tạo biến loại được định lượng. Ví dụ: người gọi hàm nhận dạng, id id :: ∀aa → a, xác định loại cần chọn cho biến loại a cho ứng dụng id cụ thể này. Đối với ứng dụng chức năng, id id 3, loại này bằng Int.

  • Việc tạo ra một giá trị với kiểu ∃ định lượng xác định và ẩn, kiểu của biến loại được định lượng. Ví dụ: một người tạo ra một “a. (A, a → Int) có thể đã xây dựng một giá trị của loại đó từ Hồi (3, λx → x) một người tạo khác đã xây dựng một giá trị với cùng loại từ ((x x, λx → ord x). Từ quan điểm người dùng, cả hai giá trị có cùng loại và do đó có thể hoán đổi cho nhau. Giá trị có một loại cụ thể được chọn cho biến loại a, nhưng chúng tôi không biết loại nào, vì vậy thông tin này không còn có thể được khai thác. Thông tin loại giá trị cụ thể này đã bị 'lãng quên'; chúng tôi chỉ biết nó tồn tại.


1
Mặc dù liên kết này có thể trả lời câu hỏi, tốt hơn là bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo. Câu trả lời chỉ liên kết có thể trở nên không hợp lệ nếu trang được liên kết thay đổi.
sheilak 04/11/2015

1
@sheilak: đã cập nhật câu trả lời, cảm ơn vì lời đề nghị
themarketka

5

Một loại phổ biến tồn tại cho tất cả các giá trị của (các) tham số loại. Một kiểu tồn tại chỉ tồn tại cho các giá trị của (các) tham số loại thỏa mãn các ràng buộc của kiểu tồn tại.

Ví dụ, trong Scala, một cách để thể hiện một kiểu tồn tại là một kiểu trừu tượng bị ràng buộc với một số giới hạn trên hoặc dưới.

trait Existential {
  type Parameter <: Interface
}

Tương đương một loại phổ quát bị ràng buộc là một loại tồn tại như trong ví dụ sau.

trait Existential[Parameter <: Interface]

Bất kỳ trang web sử dụng nào cũng có thể sử dụng Interfacebởi vì bất kỳ kiểu con khả thi nào Existentialphải xác định type Parametercái phải thực hiện Interface.

Một trường hợp suy biến của một kiểu tồn tại trong Scala là một kiểu trừu tượng không bao giờ được đề cập và do đó không cần phải được định nghĩa bởi bất kỳ kiểu con nào. Điều này thực sự có một ký hiệu viết tắt List[_] trong ScalaList<?>trong Java.

Câu trả lời của tôi được lấy cảm hứng từ đề xuất của Martin Oderky để thống nhất các loại trừu tượng và hiện sinh. Các slide hỗ trợ hiểu biết.


1
Đọc qua một số tài liệu ở trên, có vẻ như bạn đã tóm tắt một cách độc đáo sự hiểu biết của tôi: Các loại phổ quát ∀x.f(x), mờ đục đối với các chức năng nhận của chúng trong khi Các loại Hiện sinh ∃x.f(x), bị hạn chế để có các thuộc tính nhất định. Thông thường, tất cả các tham số là Existential vì chức năng của chúng sẽ thao tác trực tiếp với chúng; tuy nhiên, các tham số chung có thể có các loại là Universal vì hàm sẽ không quản lý chúng ngoài các hoạt động phổ quát cơ bản như lấy tham chiếu như trong:∀x.∃array.copy(src:array[x] dst:array[x]){...}
George

Như được mô tả ở đây stackoverflow.com/a/19413755/3195266 thành viên loại có thể mô phỏng định lượng phổ quát thông qua loại nhận dạng. Và chắc chắn có forSomeđịnh lượng tồn tại tham số loại.
Netsu

3

Nghiên cứu về các kiểu dữ liệu trừu tượng và ẩn thông tin đã đưa các kiểu tồn tại vào các ngôn ngữ lập trình. Tạo một kiểu tóm tắt kiểu dữ liệu ẩn thông tin về kiểu đó, vì vậy một máy khách kiểu đó không thể lạm dụng nó. Giả sử bạn đã có một tham chiếu đến một đối tượng ... một số ngôn ngữ cho phép bạn chuyển tham chiếu đó đến một tham chiếu đến byte và làm bất cứ điều gì bạn muốn với phần bộ nhớ đó. Với mục đích đảm bảo hành vi của một chương trình, ngôn ngữ sẽ hữu ích khi bạn thực thi rằng bạn chỉ hành động dựa trên tham chiếu đến đối tượng thông qua các phương thức mà người thiết kế đối tượng cung cấp. Bạn biết loại tồn tại, nhưng không có gì hơn.

Xem:

Các loại trừu tượng có Loại Hiện tại, MITCHEL & PLOTKIN

http://theory.stanford.edu/~jcm/ con / mitch-plotkin-88.pdf


1

Tôi đã tạo ra sơ đồ này. Tôi không biết nếu nó nghiêm ngặt. Nhưng nếu nó giúp, tôi rất vui. nhập mô tả hình ảnh ở đây


-6

Theo tôi hiểu đó là một cách toán học để mô tả các giao diện / lớp trừu tượng.

Còn đối với T = ∃X {X a; int f (X); }

Đối với C #, nó sẽ dịch sang một loại trừu tượng chung:

abstract class MyType<T>{
    private T a;

    public abstract int f(T x);
}

"Hiện sinh" chỉ có nghĩa là có một số loại tuân theo các quy tắc được xác định ở đây.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.