ประเภทอัตถิภาวนิยมคืออะไร?


171

ผมอ่านผ่านบทความวิกิพีเดียประเภทอัตถิภาวนิยม ฉันรวบรวมว่าพวกเขาเรียกว่าประเภทอัตถิภาวนิยมเนื่องจากตัวดำเนินการที่มีอยู่ (∃) ฉันไม่แน่ใจว่าประเด็นของมันคืออะไร ความแตกต่างระหว่างอะไร

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

และ

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

?


8
นี่อาจเป็นหัวข้อที่ดีสำหรับ programmers.stackexchange.com programmers.stackexchange.com
jpierson

คำตอบ:


192

เมื่อมีคนกำหนดประเภทสากลที่∀Xพวกเขากำลังพูดว่า: คุณสามารถเสียบประเภทใดก็ได้ที่คุณต้องการฉันไม่จำเป็นต้องรู้อะไรเกี่ยวกับประเภทที่จะทำงานของฉันฉันจะพูดถึงมันอย่างเปิดเผยXเท่านั้น

เมื่อมีคนกำหนดประเภทของการดำรงอยู่ที่∃Xพวกเขากำลังพูดว่า: ฉันจะใช้สิ่งที่ฉันต้องการที่นี่ คุณเคยรู้อะไรเกี่ยวกับชนิดเพื่อให้คุณสามารถเรียกมัน opaquely Xเป็น

ประเภทสากลให้คุณเขียนสิ่งต่าง ๆ เช่น:

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

copyฟังก์ชั่นที่มีความคิดในสิ่งที่ไม่มีTจริงจะเป็น แต่มันไม่จำเป็นต้อง

ประเภทที่มีอยู่จะช่วยให้คุณเขียนสิ่งต่าง ๆ เช่น:

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

การใช้งานเครื่องเสมือนแต่ละรายการสามารถมีประเภท bytecode ที่แตกต่างกัน runAllCompilersฟังก์ชั่นที่มีความคิดว่าสิ่งที่ประเภท bytecode คือไม่มี แต่มันไม่จำเป็นต้อง; ทั้งหมดมันไม่เป็นถ่ายทอด bytecode จากไปVirtualMachine.compileVirtualMachine.run

อักขระตัวแทนชนิด Java (ex:) List<?>เป็นรูปแบบที่ จำกัด มากของประเภทที่มีอยู่

อัปเดต:ลืมที่จะพูดถึงว่าคุณสามารถจัดเรียงของประเภทที่มีอยู่กับประเภทสากล ก่อนอื่นให้ห่อประเภทสากลของคุณเพื่อซ่อนพารามิเตอร์ประเภท ประการที่สองการควบคุมการกลับด้าน (การสลับส่วน "คุณ" และ "ฉัน" ได้อย่างมีประสิทธิภาพในคำจำกัดความข้างต้นซึ่งเป็นความแตกต่างหลักระหว่างอัตถิภาวนิยมกับจักรวาล)

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

ตอนนี้เราสามารถมีVMWrapperสายของเราเองVMHandlerซึ่งมีhandleฟังก์ชั่นที่พิมพ์สากล ผลสุทธิเหมือนกันรหัสของเราจะต้องถือว่าBเป็นทึบแสง

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

ตัวอย่างการนำ 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
@Kanan, +1 สำหรับคำตอบที่มีประโยชน์มาก แต่ค่อนข้างยากที่จะเข้าใจ: 1.ฉันคิดว่ามันจะช่วยได้ถ้าคุณชัดเจนมากขึ้นเกี่ยวกับธรรมชาติของการมีอยู่และประเภทสากล ฉันเท่านั้นที่รู้โดยบังเอิญว่าคุณเขียนประโยคสองย่อหน้าแรกในทำนองเดียวกันมากแค่ไหน ในภายหลังคุณจะระบุอย่างชัดเจนว่าคำจำกัดความทั้งสองนั้นเหมือนกัน แต่กับ "ฉัน" และ "คุณ" กลับด้าน นอกจากนี้ฉันไม่เข้าใจทันทีว่า "ฉัน" และ "คุณ" ควรอ้างถึงอะไร
stakx - ไม่สนับสนุน

2
(ต่อ :) 2.ผมไม่เข้าใจความหมายของสัญกรณ์คณิตศาสตร์ในหรือList<∃B:VirtualMachine<B>> vms for (∃B:VirtualMachine<B> vm : vms)(เนื่องจากสิ่งเหล่านี้เป็นประเภททั่วไปคุณไม่สามารถใช้?สัญลักษณ์ตัวแทนของ Java แทนไวยากรณ์ "ที่ทำเอง" ได้หรือไม่) ฉันคิดว่ามันอาจช่วยให้มีตัวอย่างรหัสที่ไม่มีประเภททั่วไปเช่น∃B:VirtualMachine<B>มีส่วนเกี่ยวข้อง แต่แทนที่จะเป็น "ตรง" ∃Bเนื่องจากประเภททั่วไปเชื่อมโยงกับประเภทสากลได้ง่ายหลังจากตัวอย่างรหัสแรกของคุณ
stakx - ไม่สนับสนุน

2
ฉันเคย∃Bมีความชัดเจนเกี่ยวกับปริมาณที่เกิดขึ้น ด้วยไวลด์การ์ดไวยากรณ์ปริมาณจะถูกบอกเป็นนัย ( List<List<?>>จริง ๆ แล้วหมายถึง∃T:List<List<T>>และไม่ใช่List<∃T:List<T>>) นอกจากนี้ปริมาณที่ชัดเจนช่วยให้คุณสามารถอ้างถึงประเภท (ฉันปรับเปลี่ยนตัวอย่างเพื่อใช้ประโยชน์จากสิ่งนี้โดยการจัดเก็บชนิดของรหัสBในตัวแปรชั่วคราว)
Kannan Goundan

2
สัญกรณ์ทางคณิตศาสตร์ที่ใช้ที่นี่เป็นเลอะเทอะเหมือนนรก แต่ฉันไม่คิดว่านั่นเป็นความผิดของผู้ตอบคำถาม (เป็นมาตรฐาน) ยังคงที่ดีที่สุดที่จะไม่ละเมิดปริมาณการดำรงอยู่และสากลในลักษณะดังกล่าวอาจจะ ...
Noldorin

2
@Kanan_Goundan ฉันต้องการรู้ว่าอะไรทำให้คุณพูดว่า Java wildcard เป็นเวอร์ชันที่ จำกัด มาก คุณรู้หรือไม่ว่าคุณสามารถใช้ฟังก์ชั่นตัวอย่าง runAllCompilers ตัวอย่างแรกในจาวาบริสุทธิ์ (ด้วยฟังก์ชั่นตัวช่วยในการดึง (ตั้งชื่อ) พารามิเตอร์ wilcard)
LP_

107

ค่าของประเภทอัตถิภาวนิยมเหมือน∃x. F(x) เป็นคู่ที่มีบางชนิด xและมูลค่าF(x)ของประเภท ในขณะที่มูลค่าของประเภท polymorphic เหมือน∀x. F(x)เป็นฟังก์ชั่นที่ใช้บางชนิดxและผลิตF(x)ตามตัวอักษรประเภท Fในทั้งสองกรณีประเภทปิดมากกว่าบางประเภทคอนสตรัค

โปรดทราบว่ามุมมองนี้จะผสมประเภทและค่าต่างๆ หลักฐานการดำรงอยู่คือหนึ่งประเภทและหนึ่งค่า การพิสูจน์สากลคือกลุ่มของค่าทั้งหมดที่จัดทำดัชนีตามประเภท (หรือการแมปจากประเภทถึงค่า)

ดังนั้นความแตกต่างระหว่างสองประเภทที่คุณระบุจึงเป็นดังนี้:

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

ซึ่งหมายความว่าค่าชนิดTมีรูปแบบที่เรียกว่าXค่าและฟังก์ชั่นa:X f:X->intผู้ผลิตค่านิยมประเภทTจะเลือกประเภทใดก็ได้Xและผู้บริโภคไม่รู้อะไรXเลย ยกเว้นว่ามีหนึ่งในตัวอย่างของมันเรียกว่าaและว่าค่านี้สามารถกลายเป็นโดยให้มันไปint fกล่าวอีกนัยหนึ่งค่าประเภทTรู้วิธีการผลิตintอย่างใด ทีนี้เราสามารถกำจัดชนิดตัวกลางXและเพียงแค่พูดว่า:

T = int

ปริมาณที่เป็นสากลนั้นแตกต่างกันเล็กน้อย

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

ซึ่งหมายความว่าค่าของประเภทTจะได้รับประเภทใดXและมันจะก่อให้เกิดความคุ้มค่าa:Xและฟังก์ชั่นf:X->int ไม่ว่าสิ่งที่Xเป็น ในคำอื่น ๆ : ผู้บริโภคของค่าประเภทสามารถเลือกประเภทสำหรับการใดTXและผู้ผลิตของค่าชนิดTไม่สามารถรู้อะไรเกี่ยวกับเรื่องทั้งหมดXแต่มันจะต้องมีความสามารถในการผลิตค่าaสำหรับทางเลือกใด ๆและจะสามารถเปิดดังกล่าวมีมูลค่าเป็นXint

เห็นได้ชัดว่าการใช้งานประเภทนี้เป็นไปไม่ได้เพราะไม่มีโปรแกรมใดที่สามารถสร้างมูลค่าให้กับทุกประเภทเท่าที่จะจินตนาการได้ นอกจากว่าคุณจะไร้สาระเช่นเดียวกับnullหรือพื้น

ตั้งแต่การดำรงอยู่เป็นคู่อาร์กิวเมนต์อัตถิภาวนิยมสามารถแปลงเป็นหนึ่งสากลผ่านcurrying

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

เหมือนกับ:

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

อดีตเป็นอันดับ 2อัตถิภาวนิยม สิ่งนี้นำไปสู่คุณสมบัติที่มีประโยชน์ต่อไปนี้:

ทุกประเภทปริมาณ existentially ของการจัดอันดับเป็นชนิดปริมาณในระดับสากลของการจัดอันดับn+1n

มีขั้นตอนวิธีการมาตรฐานสำหรับการเปิด existentials เข้าสู่สากลเป็นที่เรียกว่าSkolemization


7
อาจเป็นประโยชน์ (หรือไม่ก็ได้) ในการพูดถึง Skolemization en.wikipedia.org/wiki/Skolem_normal_form
Geoff Reedy

34

ฉันคิดว่ามันสมเหตุสมผลที่จะอธิบายประเภทอัตถิภาวนิยมพร้อมกับประเภทสากลเนื่องจากแนวคิดทั้งสองนั้นเป็นส่วนเสริมนั่นคือหนึ่งคือ "ตรงกันข้าม" ของอีกประเภทหนึ่ง

ฉันไม่สามารถตอบทุกรายละเอียดเกี่ยวกับประเภทการดำรงอยู่ (เช่นการให้คำจำกัดความที่แน่นอนรายการการใช้งานที่เป็นไปได้ทั้งหมดความสัมพันธ์กับประเภทข้อมูลนามธรรม ฯลฯ ) เพราะฉันไม่มีความรู้เพียงพอสำหรับเรื่องนั้น ฉันจะแสดงเฉพาะ (โดยใช้ Java) สิ่งที่บทความ HaskellWiki นี้ระบุว่าเป็นผลหลักของประเภทที่มีอยู่:

ประเภทที่มีอยู่สามารถใช้เพื่อวัตถุประสงค์ที่แตกต่างกันหลายอย่าง แต่สิ่งที่พวกเขาทำคือ 'ซ่อน' ตัวแปรประเภททางด้านขวา โดยปกติตัวแปรประเภทใด ๆ ที่ปรากฏทางด้านขวาจะต้องปรากฏขึ้นทางด้านซ้าย […]

ตัวอย่างการตั้งค่า:

หลอกรหัสต่อไปนี้ไม่ถูกต้อง Java แม้ว่ามันจะง่ายพอที่จะแก้ไขได้ ในความเป็นจริงนั่นคือสิ่งที่ฉันจะทำในคำตอบนี้!

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

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

ให้ฉันสะกดสิ่งนี้สั้น ๆ ให้คุณ เรากำลังกำหนด ...

  • ประเภทเรียกซ้ำTree<α>ซึ่งแสดงถึงโหนดในต้นไม้ไบนารี แต่ละโหนดเก็บ a αvalueบางประเภทและมีการอ้างอิงถึงทางเลือกและsubtrees ประเภทเดียวกันleftright

  • ฟังก์ชั่นheightที่ส่งกลับระยะทางไกลที่สุดจากโหนดใบใด ๆ ไปยังโหนดรูt

ตอนนี้เราลองเปลี่ยนโค้ดหลอกข้างต้นheightเป็นไวยากรณ์ Java ที่ถูกต้อง! (ฉันจะไม่ละส่วนสำเร็จรูปบางส่วนเพื่อประโยชน์ของความกะทัดรัดเช่นการวางแนววัตถุและตัวดัดแปลงการช่วยสำหรับการเข้าถึง) ฉันจะแสดงโซลูชันที่เป็นไปได้สองแบบ

1. ประเภทโซลูชั่นสากล:

การแก้ไขที่ชัดเจนที่สุดคือการสร้างheightทั่วไปโดยการแนะนำพารามิเตอร์ type αลงในลายเซ็น:

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

สิ่งนี้จะช่วยให้คุณสามารถประกาศตัวแปรและสร้างนิพจน์ประเภทαภายในฟังก์ชันนั้นหากคุณต้องการ แต่...

2. การแก้ปัญหาประเภทที่มีอยู่:

หากคุณดูที่เนื้อหาของวิธีการของเราคุณจะสังเกตเห็นว่าเราไม่ได้เข้าถึงหรือทำงานด้วยประเภทα ! ไม่มีนิพจน์ที่มีประเภทนั้นหรือตัวแปรที่ประกาศด้วยประเภทนั้น ... ดังนั้นทำไมเราต้องสร้างheightประโยคทั่วไป ทำไมเราถึงลืมαไม่ได้? ตามที่ปรากฎเราสามารถ:

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

ขณะที่ฉันเขียนเมื่อตอนต้นของคำตอบนี้ประเภทของอัตถิภาวนิยมและสากลนั้นเป็นส่วนประกอบที่สมบูรณ์ / คู่ในธรรมชาติ ดังนั้นหากการแก้ปัญหาประเภทสากลคือการทำให้height มากขึ้นทั่วไปแล้วเราควรคาดหวังว่าประเภทอัตถิภาวนิยมมีผลตรงข้าม: ทำให้มันน้อยทั่วไปคือโดยการซ่อน / ลบชนิดพารามิเตอร์α

ด้วยเหตุนี้คุณไม่สามารถอ้างถึงประเภทของt.valueวิธีการนี้ได้อีกต่อไปหรือจัดการกับการแสดงออกประเภทนั้นอีกต่อไปเพราะไม่มีตัวระบุที่ถูกผูกไว้กับมัน ( ?อักขระตัวแทนเป็นโทเค็นพิเศษไม่ใช่ตัวระบุที่ "จับ" ประเภท) t.valueได้กลายเป็นทึบแสงอย่างมีประสิทธิภาพ Objectอาจจะเป็นสิ่งเดียวที่คุณยังคงสามารถทำอะไรกับมันเป็นประเภทหล่อมัน

สรุป:

===========================================================
                     |    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
คำอธิบายที่ดี คุณไม่จำเป็นต้องร่าย t.value ไปที่ Object คุณสามารถเรียกมันว่า Object ได้ ฉันจะบอกว่าประเภทอัตถิภาวนิยมทำให้วิธีการทั่วไปมากขึ้นเพราะการที่ สิ่งเดียวที่คุณเคยรู้เกี่ยวกับ t.value คือมันเป็นวัตถุในขณะที่คุณสามารถพูดอะไรบางอย่างที่เฉพาะเจาะจงเกี่ยวกับα (เช่นαขยาย Serializable)
Craig P. Motlin

1
ขณะที่ฉันได้มาเชื่อว่าคำตอบของฉันไม่ได้จริงๆอธิบายสิ่งที่ existentials มีและฉันกำลังพิจารณาการเขียนอีกคนหนึ่งที่มากขึ้นเช่นสองย่อหน้าแรกของคำตอบคาน Goudan ซึ่งผมคิดว่าเป็นผู้ใกล้ชิดกับ "ความจริง" ที่ถูกกล่าวว่า @Craig: การเปรียบเทียบข้อมูลทั่วไปกับObjectค่อนข้างน่าสนใจ: ในขณะที่ทั้งสองมีความคล้ายคลึงกันในการที่พวกเขาช่วยให้คุณสามารถเขียนรหัสอิสระประเภทคงที่, อดีต (generics) ไม่เพียงทิ้งข้อมูลประเภทเกือบทั้งหมดไป บรรลุเป้าหมายนี้ ในความหมายเฉพาะนี้ยาชื่อสามัญเป็นยาของObjectIMO
stakx - ไม่ร่วมให้ข้อมูลอีก

1
@stakx ในรหัสนี้ (จากที่มีประสิทธิภาพ Java) 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))); } , Eเป็นuniversal typeและ?หมายถึงexistential type?
เควินเมเรดิ ธ

คำตอบนี้ไม่ถูกต้อง ?ในประเภทint height(Tree<?> t)ยังคงไม่เป็นที่รู้จักภายในฟังก์ชั่นและยังคงถูกกำหนดโดยผู้ที่โทรมาเพราะมันเป็นโทรที่มีการเลือกต้นไม้ที่จะผ่านใน. แม้ว่าคนเรียกนี้ชนิดที่ดำรงอยู่ใน Java มันไม่ได้เป็น ?ยึดสามารถนำมาใช้ในการดำเนินการในรูปแบบของ existentials ใน Java ในบางสถานการณ์ แต่นี้ไม่ได้เป็นหนึ่งในพวกเขา
Peter Hall

15

ทั้งหมดนี้เป็นตัวอย่างที่ดี แต่ฉันเลือกที่จะตอบต่างออกไปเล็กน้อย จำได้จากคณิตศาสตร์ว่า∀x P (x) หมายถึง "สำหรับ x ทั้งหมดฉันสามารถพิสูจน์ได้ว่า P (x)" มันคือฟังก์ชั่นชนิดหนึ่งคุณให้ x แก่ฉันและฉันมีวิธีพิสูจน์มันให้คุณ

ในทฤษฎีประเภทเราไม่ได้พูดถึงการพิสูจน์ แต่เป็นประเภท ดังนั้นในพื้นที่นี้เราหมายถึง "สำหรับประเภท X ใด ๆ ที่คุณให้ฉันฉันจะให้ประเภท P เฉพาะ" ตอนนี้เนื่องจากเราไม่ได้ให้ข้อมูล P มากเกี่ยวกับ X นอกเหนือจากความจริงที่ว่ามันเป็นประเภท P ไม่สามารถทำอะไรได้มาก แต่มีบางตัวอย่าง P สามารถสร้างชนิดของ "คู่ทุกชนิดเดียวกัน" P<X> = Pair<X, X> = (X, X)ไปนี้: หรือเราสามารถสร้างประเภทตัวเลือก: P<X> = Option<X> = X | Nilโดยที่ Nil เป็นประเภทของพอยน์เตอร์พอยน์เตอร์ List<X> = (X, List<X>) | Nilเราสามารถทำรายการออกจากมัน: ขอให้สังเกตว่าอันสุดท้ายคือเรียกซ้ำค่าของList<X>ทั้งคู่ที่องค์ประกอบแรกคือ X และองค์ประกอบที่สองคือList<X>หรืออย่างอื่นมันเป็นตัวชี้โมฆะ

ตอนนี้ในคณิตศาสตร์∃x P (x) หมายถึง "ฉันสามารถพิสูจน์ได้ว่ามี x เฉพาะเช่นที่ P (x) เป็นจริง" อาจมี x จำนวนมาก แต่เพื่อพิสูจน์ว่ามีเพียงพอ อีกวิธีที่จะคิดว่ามันจะต้องมีชุดหลักฐานและหลักฐานที่ไม่ว่างเปล่า {(x, P (x))}

แปลเป็นทฤษฎีประเภท: ประเภทในตระกูล∃X.P<X>คือประเภท X และประเภทที่P<X>เกี่ยวข้อง สังเกตว่าก่อนที่เราจะให้ X แก่ P (เพื่อให้เรารู้ทุกอย่างเกี่ยวกับ X แต่ P น้อยมาก) ว่าสิ่งที่ตรงกันข้ามนั้นเป็นจริง P<X>ไม่สัญญาว่าจะให้ข้อมูลใด ๆ เกี่ยวกับ X เพียงแค่มีสิ่งนั้นและมันเป็นประเภทแน่นอน

สิ่งนี้มีประโยชน์อย่างไร? ทีนี้, P น่าจะเป็นประเภทที่มีวิธีเปิดเผยประเภทภายในของ X ตัวอย่างจะเป็นวัตถุที่ซ่อนการเป็นตัวแทนภายในของสถานะ X แม้ว่าเราจะไม่มีวิธีจัดการกับมันโดยตรง แต่เราสามารถสังเกตผลของมันโดย poking ที่ P. การใช้งานประเภทนี้มีอยู่มากมาย แต่คุณสามารถใช้งานได้ทุกประเภทไม่ว่าจะเลือกประเภทใด


2
อืม แต่สิ่งที่ฟังก์ชั่นได้รับจากการรู้ว่ามันคืออะไรP<X>แทนที่จะเป็นP(ฟังก์ชั่นและประเภทของคอนเทนเนอร์เดียวกันสมมุติว่า แต่คุณไม่รู้ว่ามันมีXอะไร)
Claudiu

3
การพูดอย่างเคร่งครัด∀x. P(x)ไม่ได้มีความหมายอะไรเกี่ยวกับการพิสูจน์P(x)ความจริงเท่านั้น
. GitHub หยุดช่วยน้ำแข็ง

11

หากต้องการตอบคำถามของคุณโดยตรง:

มีประเภทสากลใช้ต้องมีพารามิเตอร์ชนิดT Xยกตัวอย่างเช่นหรือT<String> T<Integer>สำหรับการใช้งานประเภทที่มีอยู่ของTไม่รวมพารามิเตอร์ประเภทนั้นเพราะมันไม่เป็นที่รู้จักหรือไม่เกี่ยวข้อง - เพียงแค่ใช้T(หรือใน Java T<?>)

ข้อมูลเพิ่มเติม:

ประเภทสากล / นามธรรมและประเภทที่มีอยู่เป็นคู่ของมุมมองระหว่างผู้บริโภค / ลูกค้าของวัตถุ / ฟังก์ชั่นและผู้ผลิต / การใช้งานของมัน เมื่อด้านใดด้านหนึ่งเห็นประเภทสากลอีกด้านหนึ่งจะเห็นประเภทที่มีอยู่

ใน Java คุณสามารถกำหนดคลาสทั่วไป:

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();
  • จากมุมมองของที่ลูกค้าของMyClass, Tเป็นสากลเพราะคุณสามารถทดแทนประเภทใดTเมื่อคุณใช้คลาสที่และคุณจะต้องรู้ชนิดที่เกิดขึ้นจริงของ T เมื่อใดก็ตามที่คุณใช้ตัวอย่างของMyClass
  • จากมุมมองของวิธีการอินสแตนซ์ในMyClassตัวเองTมีอยู่เพราะไม่ทราบชนิดที่แท้จริงของT
  • ใน Java ?หมายถึงประเภทอัตถิภาวนิยม - ดังนั้นเมื่อคุณอยู่ในระดับที่เป็นพื้นT ?หากคุณต้องการจัดการกับอินสแตนซ์ที่MyClassมีTอยู่คุณสามารถประกาศMyClass<?>ดังsecretMessage()ตัวอย่างข้างต้น

บางครั้งประเภทที่มีอยู่จะถูกใช้เพื่อซ่อนรายละเอียดการใช้งานของบางสิ่งบางอย่างตามที่กล่าวไว้ที่อื่น Java เวอร์ชันนี้อาจมีลักษณะดังนี้:

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

มันค่อนข้างยุ่งยากในการจับภาพอย่างถูกต้องเพราะฉันแกล้งทำเป็นภาษาโปรแกรมที่ใช้งานได้บางประเภทซึ่ง Java ไม่ได้เป็นเช่นนั้น แต่ประเด็นตรงนี้คือคุณกำลังจับสถานะบางอย่างพร้อมกับรายการฟังก์ชั่นที่ทำงานในสถานะนั้นและคุณไม่ทราบประเภทที่แท้จริงของส่วนของรัฐ แต่ฟังก์ชั่นทำเนื่องจากจับคู่กับประเภทนั้นแล้ว .

ตอนนี้ใน Java ทุกประเภทที่ไม่ใช่ขั้นสุดท้ายไม่ได้มีอยู่บางส่วน สิ่งนี้อาจฟังดูแปลก แต่เนื่องจากตัวแปรที่ประกาศว่าObjectอาจเป็นคลาสย่อยของObjectแทนคุณจึงไม่สามารถประกาศประเภทเฉพาะได้เฉพาะ "ประเภทนี้หรือคลาสย่อย" ดังนั้นวัตถุจึงถูกแทนด้วยสถานะของบิตพร้อมกับรายการฟังก์ชันที่ใช้งานในสถานะนั้น - ซึ่งฟังก์ชันที่จะเรียกถูกกำหนดที่รันไทม์โดยการค้นหา นี่เหมือนกับการใช้งานประเภทที่มีอยู่ด้านบนซึ่งคุณมีส่วนสถานะที่เป็นอยู่และฟังก์ชันที่ทำงานกับสถานะนั้น

ในภาษาการเขียนโปรแกรมแบบคงที่โดยไม่ต้องพิมพ์ย่อยและปลดเปลื้องประเภทที่มีอยู่ช่วยให้หนึ่งในการจัดการรายการของวัตถุที่พิมพ์แตกต่างกัน รายการไม่สามารถมีT<Int> T<Long>อย่างไรก็ตามรายการของT<?>สามารถมีรูปแบบใด ๆ ของการTอนุญาตให้หนึ่งที่จะใส่ข้อมูลหลายประเภทลงในรายการและแปลงพวกเขาทั้งหมดเป็น int (หรือการดำเนินการใด ๆ ที่มีให้ภายในโครงสร้างข้อมูล) ตามความต้องการ

เราสามารถแปลงระเบียนด้วยประเภทที่มีอยู่ให้เป็นระเบียนได้โดยไม่ต้องใช้การปิด การปิดการพิมพ์นั้นมีอยู่จริงเช่นกันซึ่งตัวแปรอิสระที่ถูกปิดจะถูกซ่อนจากผู้โทร ดังนั้นภาษาที่สนับสนุนการปิด แต่ไม่มีประเภทที่มีอยู่สามารถช่วยให้คุณทำการปิดที่ใช้ร่วมกันสถานะที่ซ่อนอยู่เดียวกับที่คุณจะใส่ลงในส่วนที่มีอยู่ของวัตถุ


11

ประเภทที่มีอยู่เป็นประเภทที่ทึบแสง

คิดถึงการจัดการไฟล์ใน Unix คุณรู้ว่าประเภทของมันเป็นแบบ int ดังนั้นคุณสามารถปลอมแปลงได้ ตัวอย่างเช่นคุณสามารถลองอ่านจากหมายเลขอ้างอิง 43 หากเกิดขึ้นว่าโปรแกรมมีไฟล์ที่เปิดด้วยหมายเลขอ้างอิงเฉพาะนี้คุณจะอ่านจากมัน รหัสของคุณไม่จำเป็นต้องเป็นอันตรายเพียงเลอะเทอะ (เช่นที่จับอาจเป็นตัวแปรที่ไม่มีการกำหนดค่าเริ่มต้น)

ประเภทที่มีอยู่ถูกซ่อนจากโปรแกรมของคุณ หากfopenคืนค่าประเภทที่มีอยู่ทั้งหมดที่คุณสามารถทำได้คือใช้กับฟังก์ชันบางอย่างของไลบรารีที่ยอมรับประเภทที่มีอยู่นี้ ตัวอย่างเช่นหลอกรหัสต่อไปนี้จะรวบรวม:

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

อินเตอร์เฟส "read" ถูกประกาศเป็น:

มีประเภท T เช่นนั้น:

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

ตัวแปร exfile ไม่ใช่ int ไม่ใช่ a char*ไม่ใช่ Struct ไฟล์ไม่มีอะไรที่คุณสามารถแสดงได้ในระบบ type คุณไม่สามารถประกาศตัวแปรที่ไม่ทราบชนิดและไม่สามารถแปลงพูดตัวชี้ไปเป็นประเภทที่ไม่รู้จัก ภาษาจะไม่ยอมให้คุณ


9
สิ่งนี้ใช้ไม่ได้ หากลายเซ็นของreadคือ∃T.read(T file, ...)ไม่มีอะไรที่คุณสามารถผ่านเป็นพารามิเตอร์แรก สิ่งที่จะได้ผลคือการfopenคืนค่าหมายเลขอ้างอิงของไฟล์และฟังก์ชั่นการอ่านที่มีขอบเขตเดียวกันโดยมีอยู่จริง :∃T.(T, read(T file, ...))
Kannan Goundan

2
นี่ดูเหมือนจะเป็นแค่การพูดคุยเกี่ยวกับ ADT
kizzx2

7

ดูเหมือนว่าฉันจะมาสายแต่ทว่าเอกสารนี้จะเพิ่มมุมมองอีกประเภทของการดำรงอยู่แม้ว่าจะไม่ได้เป็นผู้ไม่เชื่อเรื่องภาษาโดยเฉพาะมันควรจะง่ายกว่าที่จะเข้าใจประเภทการดำรงอยู่: http: //www.cs.uu .nl / groups / ST / Projects / ehc / ehc-book.pdf (ตอนที่ 8)

ความแตกต่างระหว่างชนิดปริมาณที่เป็นสากลและที่มีอยู่สามารถระบุได้ด้วยการสังเกตต่อไปนี้:

  • การใช้ค่าที่มีประเภทเชิงปริมาณนั้นเป็นตัวกำหนดประเภทที่จะเลือกสำหรับการเริ่มต้นตัวแปรตัวแปรเชิงปริมาณ ตัวอย่างเช่นผู้เรียกฟังก์ชั่นระบุตัวตน“ id :: ∀aa→ a” กำหนดประเภทที่จะเลือกสำหรับตัวแปรประเภท a สำหรับแอปพลิเคชันของรหัสเฉพาะนี้ สำหรับแอปพลิเคชั่นฟังก์ชั่น“ id 3” ประเภทนี้เท่ากับ Int

  • การสร้างค่าที่มีประเภทเชิงปริมาณนั้นเป็นตัวกำหนดและซ่อนชนิดของตัวแปรชนิดที่ถูกวัด ตัวอย่างเช่นผู้สร้าง“ ∃a. (a, a → Int)” อาจสร้างมูลค่าของประเภทนั้นจาก“ (3, λx→ x)”; ผู้สร้างรายอื่นได้สร้างมูลค่าด้วยประเภทเดียวกันจาก“ ('x', λx→ ord x)” จากมุมมองของผู้ใช้ค่าทั้งสองมีชนิดเดียวกันและสามารถใช้แทนกันได้ ค่ามีประเภทเฉพาะที่เลือกสำหรับตัวแปรประเภท a แต่เราไม่ทราบว่าเป็นประเภทใดดังนั้นข้อมูลนี้จึงไม่สามารถใช้ประโยชน์ได้อีกต่อไป ข้อมูลประเภทค่าเฉพาะนี้ถูก 'ลืม'; เรารู้แค่ว่ามันมีอยู่จริง


1
แม้ว่าลิงก์นี้อาจตอบคำถามได้ดีกว่าหากรวมส่วนสำคัญของคำตอบไว้ที่นี่และให้ลิงก์สำหรับการอ้างอิง คำตอบสำหรับลิงก์เท่านั้นอาจไม่ถูกต้องหากหน้าเว็บที่เชื่อมโยงมีการเปลี่ยนแปลง
sheilak

1
@sheilak: อัปเดตคำตอบขอขอบคุณสำหรับคำแนะนำ
themarketka

5

มีประเภทสากลสำหรับค่าทั้งหมดของพารามิเตอร์ประเภท ประเภทที่มีอยู่มีอยู่เฉพาะสำหรับค่าของพารามิเตอร์ประเภทที่ตอบสนองข้อ จำกัด ของประเภทที่มีอยู่

ตัวอย่างเช่นใน Scala วิธีหนึ่งในการแสดงประเภทที่มีอยู่เป็นประเภทนามธรรมซึ่งถูก จำกัด ให้บางขอบเขตบนหรือล่าง

trait Existential {
  type Parameter <: Interface
}

ประเภทสากลที่ถูก จำกัด เท่ากับประเภทที่มีอยู่ดังในตัวอย่างต่อไปนี้

trait Existential[Parameter <: Interface]

ไซต์ใช้งานใด ๆ สามารถใช้งานได้Interfaceเนื่องจากExistentialต้องระบุประเภทย่อยที่สามารถtype Parameterใช้งานInterfaceได้ทันที

กรณีเลวของประเภทของการดำรงอยู่ใน Scala เป็นประเภทนามธรรมซึ่งไม่เคยเรียกจึงไม่จำเป็นต้องกำหนดโดยชนิดย่อยใด ๆ สิ่งนี้มีประสิทธิภาพอย่างย่อList[_] ใน ScalaและList<?>Java

คำตอบของฉันได้รับแรงบันดาลใจจากข้อเสนอของ Martin Odersky ในการรวมเอานามธรรมและสิ่งมีชีวิตเข้าด้วยกัน ประกอบสไลด์เครื่องช่วยทำความเข้าใจ


1
หลังจากอ่านเนื้อหาข้างต้นแล้วดูเหมือนว่าคุณได้สรุปความเข้าใจของฉันไว้อย่างชัดเจนแล้ว: Universal Types, ∀x.f(x)ทึบแสงสำหรับฟังก์ชั่นการรับของพวกเขาขณะที่ประเภท Existential ∃x.f(x), ถูก จำกัด ให้มีคุณสมบัติบางอย่าง โดยปกติแล้วพารามิเตอร์ทั้งหมดเป็น Existential เนื่องจากฟังก์ชั่นของพวกเขาจะจัดการพวกเขาโดยตรง อย่างไรก็ตามพารามิเตอร์ทั่วไปอาจมีประเภทที่เป็นสากลเนื่องจากฟังก์ชั่นจะไม่จัดการกับพวกเขานอกเหนือจากการดำเนินงานทั่วไปขั้นพื้นฐานเช่นการได้รับการอ้างอิงดังต่อไปนี้:∀x.∃array.copy(src:array[x] dst:array[x]){...}
George

ตามที่อธิบายไว้ที่นี่stackoverflow.com/a/19413755/3195266สมาชิกประเภทสามารถเลียนแบบปริมาณสากลผ่านประเภทข้อมูลประจำตัว และแน่นอนว่าforSomeสำหรับพารามิเตอร์ประเภทการวัดปริมาณที่มีอยู่
Netsu

3

การวิจัยเกี่ยวกับประเภทข้อมูลนามธรรมและการซ่อนข้อมูลได้นำประเภทของการดำรงอยู่มาสู่ภาษาการเขียนโปรแกรม การทำให้นามธรรมประเภทข้อมูลซ่อนข้อมูลเกี่ยวกับประเภทนั้นดังนั้นลูกค้าประเภทนั้นจึงไม่สามารถละเมิดได้ สมมติว่าคุณได้รับการอ้างอิงไปยังวัตถุ ... บางภาษาอนุญาตให้คุณส่งการอ้างอิงไปยังการอ้างอิงถึงไบต์และทำทุกอย่างที่คุณต้องการให้กับหน่วยความจำชิ้นนั้น สำหรับวัตถุประสงค์ในการรับประกันพฤติกรรมของโปรแกรมมันมีประโยชน์สำหรับภาษาที่จะบังคับให้คุณทำการอ้างอิงวัตถุโดยวิธีการที่นักออกแบบของวัตถุให้ คุณรู้ว่ามีอยู่จริง แต่ไม่มีอะไรเพิ่มเติม

ดู:

ประเภทนามธรรมมีประเภทที่มีอยู่ MITCHEL และพล็อต

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


1

ฉันสร้างแผนภาพนี้ ฉันไม่รู้ว่ามันเข้มงวดหรือไม่ แต่ถ้าช่วยได้ฉันก็ดีใจ ป้อนคำอธิบายรูปภาพที่นี่


-6

ในขณะที่ฉันเข้าใจว่าเป็นวิธีการทางคณิตศาสตร์ในการอธิบายอินเตอร์เฟส / คลาสนามธรรม

สำหรับ T = ∃X {X a; int f (X); }

สำหรับ C # มันจะแปลเป็นประเภทนามธรรมทั่วไป:

abstract class MyType<T>{
    private T a;

    public abstract int f(T x);
}

"Existential" เพียงหมายความว่ามีบางประเภทที่ปฏิบัติตามกฎที่กำหนดไว้ที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.