ผมอ่านผ่านบทความวิกิพีเดียประเภทอัตถิภาวนิยม ฉันรวบรวมว่าพวกเขาเรียกว่าประเภทอัตถิภาวนิยมเนื่องจากตัวดำเนินการที่มีอยู่ (∃) ฉันไม่แน่ใจว่าประเด็นของมันคืออะไร ความแตกต่างระหว่างอะไร
T = ∃X { X a; int f(X); }
และ
T = ∀x { X a; int f(X); }
?
ผมอ่านผ่านบทความวิกิพีเดียประเภทอัตถิภาวนิยม ฉันรวบรวมว่าพวกเขาเรียกว่าประเภทอัตถิภาวนิยมเนื่องจากตัวดำเนินการที่มีอยู่ (∃) ฉันไม่แน่ใจว่าประเด็นของมันคืออะไร ความแตกต่างระหว่างอะไร
T = ∃X { X a; int f(X); }
และ
T = ∀x { X a; int f(X); }
?
คำตอบ:
เมื่อมีคนกำหนดประเภทสากลที่∀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.compile
VirtualMachine.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);
}
}
List<∃B:VirtualMachine<B>> vms
for (∃B:VirtualMachine<B> vm : vms)
(เนื่องจากสิ่งเหล่านี้เป็นประเภททั่วไปคุณไม่สามารถใช้?
สัญลักษณ์ตัวแทนของ Java แทนไวยากรณ์ "ที่ทำเอง" ได้หรือไม่) ฉันคิดว่ามันอาจช่วยให้มีตัวอย่างรหัสที่ไม่มีประเภททั่วไปเช่น∃B:VirtualMachine<B>
มีส่วนเกี่ยวข้อง แต่แทนที่จะเป็น "ตรง" ∃B
เนื่องจากประเภททั่วไปเชื่อมโยงกับประเภทสากลได้ง่ายหลังจากตัวอย่างรหัสแรกของคุณ
∃B
มีความชัดเจนเกี่ยวกับปริมาณที่เกิดขึ้น ด้วยไวลด์การ์ดไวยากรณ์ปริมาณจะถูกบอกเป็นนัย ( List<List<?>>
จริง ๆ แล้วหมายถึง∃T:List<List<T>>
และไม่ใช่List<∃T:List<T>>
) นอกจากนี้ปริมาณที่ชัดเจนช่วยให้คุณสามารถอ้างถึงประเภท (ฉันปรับเปลี่ยนตัวอย่างเพื่อใช้ประโยชน์จากสิ่งนี้โดยการจัดเก็บชนิดของรหัสB
ในตัวแปรชั่วคราว)
ค่าของประเภทอัตถิภาวนิยมเหมือน∃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
เป็น ในคำอื่น ๆ : ผู้บริโภคของค่าประเภทสามารถเลือกประเภทสำหรับการใดT
ๆ X
และผู้ผลิตของค่าชนิดT
ไม่สามารถรู้อะไรเกี่ยวกับเรื่องทั้งหมดX
แต่มันจะต้องมีความสามารถในการผลิตค่าa
สำหรับทางเลือกใด ๆและจะสามารถเปิดดังกล่าวมีมูลค่าเป็นX
int
เห็นได้ชัดว่าการใช้งานประเภทนี้เป็นไปไม่ได้เพราะไม่มีโปรแกรมใดที่สามารถสร้างมูลค่าให้กับทุกประเภทเท่าที่จะจินตนาการได้ นอกจากว่าคุณจะไร้สาระเช่นเดียวกับnull
หรือพื้น
ตั้งแต่การดำรงอยู่เป็นคู่อาร์กิวเมนต์อัตถิภาวนิยมสามารถแปลงเป็นหนึ่งสากลผ่านcurrying
(∃b. F(b)) -> Int
เหมือนกับ:
∀b. (F(b) -> Int)
อดีตเป็นอันดับ 2อัตถิภาวนิยม สิ่งนี้นำไปสู่คุณสมบัติที่มีประโยชน์ต่อไปนี้:
ทุกประเภทปริมาณ existentially ของการจัดอันดับเป็นชนิดปริมาณในระดับสากลของการจัดอันดับ
n+1
n
มีขั้นตอนวิธีการมาตรฐานสำหรับการเปิด existentials เข้าสู่สากลเป็นที่เรียกว่าSkolemization
ฉันคิดว่ามันสมเหตุสมผลที่จะอธิบายประเภทอัตถิภาวนิยมพร้อมกับประเภทสากลเนื่องจากแนวคิดทั้งสองนั้นเป็นส่วนเสริมนั่นคือหนึ่งคือ "ตรงกันข้าม" ของอีกประเภทหนึ่ง
ฉันไม่สามารถตอบทุกรายละเอียดเกี่ยวกับประเภทการดำรงอยู่ (เช่นการให้คำจำกัดความที่แน่นอนรายการการใช้งานที่เป็นไปได้ทั้งหมดความสัมพันธ์กับประเภทข้อมูลนามธรรม ฯลฯ ) เพราะฉันไม่มีความรู้เพียงพอสำหรับเรื่องนั้น ฉันจะแสดงเฉพาะ (โดยใช้ 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 ประเภทเดียวกันleft
right
ฟังก์ชั่น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 |
=====================+=====================================
Object
ค่อนข้างน่าสนใจ: ในขณะที่ทั้งสองมีความคล้ายคลึงกันในการที่พวกเขาช่วยให้คุณสามารถเขียนรหัสอิสระประเภทคงที่, อดีต (generics) ไม่เพียงทิ้งข้อมูลประเภทเกือบทั้งหมดไป บรรลุเป้าหมายนี้ ในความหมายเฉพาะนี้ยาชื่อสามัญเป็นยาของObject
IMO
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 ในบางสถานการณ์ แต่นี้ไม่ได้เป็นหนึ่งในพวกเขา
ทั้งหมดนี้เป็นตัวอย่างที่ดี แต่ฉันเลือกที่จะตอบต่างออกไปเล็กน้อย จำได้จากคณิตศาสตร์ว่า∀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. การใช้งานประเภทนี้มีอยู่มากมาย แต่คุณสามารถใช้งานได้ทุกประเภทไม่ว่าจะเลือกประเภทใด
P<X>
แทนที่จะเป็นP
(ฟังก์ชั่นและประเภทของคอนเทนเนอร์เดียวกันสมมุติว่า แต่คุณไม่รู้ว่ามันมีX
อะไร)
∀x. P(x)
ไม่ได้มีความหมายอะไรเกี่ยวกับการพิสูจน์P(x)
ความจริงเท่านั้น
หากต้องการตอบคำถามของคุณโดยตรง:
มีประเภทสากลใช้ต้องมีพารามิเตอร์ชนิด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
?
หมายถึงประเภทอัตถิภาวนิยม - ดังนั้นเมื่อคุณอยู่ในระดับที่เป็นพื้น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 (หรือการดำเนินการใด ๆ ที่มีให้ภายในโครงสร้างข้อมูล) ตามความต้องการ
เราสามารถแปลงระเบียนด้วยประเภทที่มีอยู่ให้เป็นระเบียนได้โดยไม่ต้องใช้การปิด การปิดการพิมพ์นั้นมีอยู่จริงเช่นกันซึ่งตัวแปรอิสระที่ถูกปิดจะถูกซ่อนจากผู้โทร ดังนั้นภาษาที่สนับสนุนการปิด แต่ไม่มีประเภทที่มีอยู่สามารถช่วยให้คุณทำการปิดที่ใช้ร่วมกันสถานะที่ซ่อนอยู่เดียวกับที่คุณจะใส่ลงในส่วนที่มีอยู่ของวัตถุ
ประเภทที่มีอยู่เป็นประเภทที่ทึบแสง
คิดถึงการจัดการไฟล์ใน 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 คุณไม่สามารถประกาศตัวแปรที่ไม่ทราบชนิดและไม่สามารถแปลงพูดตัวชี้ไปเป็นประเภทที่ไม่รู้จัก ภาษาจะไม่ยอมให้คุณ
read
คือ∃T.read(T file, ...)
ไม่มีอะไรที่คุณสามารถผ่านเป็นพารามิเตอร์แรก สิ่งที่จะได้ผลคือการfopen
คืนค่าหมายเลขอ้างอิงของไฟล์และฟังก์ชั่นการอ่านที่มีขอบเขตเดียวกันโดยมีอยู่จริง :∃T.(T, read(T file, ...))
ดูเหมือนว่าฉันจะมาสายแต่ทว่าเอกสารนี้จะเพิ่มมุมมองอีกประเภทของการดำรงอยู่แม้ว่าจะไม่ได้เป็นผู้ไม่เชื่อเรื่องภาษาโดยเฉพาะมันควรจะง่ายกว่าที่จะเข้าใจประเภทการดำรงอยู่: 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 แต่เราไม่ทราบว่าเป็นประเภทใดดังนั้นข้อมูลนี้จึงไม่สามารถใช้ประโยชน์ได้อีกต่อไป ข้อมูลประเภทค่าเฉพาะนี้ถูก 'ลืม'; เรารู้แค่ว่ามันมีอยู่จริง
มีประเภทสากลสำหรับค่าทั้งหมดของพารามิเตอร์ประเภท ประเภทที่มีอยู่มีอยู่เฉพาะสำหรับค่าของพารามิเตอร์ประเภทที่ตอบสนองข้อ จำกัด ของประเภทที่มีอยู่
ตัวอย่างเช่นใน Scala วิธีหนึ่งในการแสดงประเภทที่มีอยู่เป็นประเภทนามธรรมซึ่งถูก จำกัด ให้บางขอบเขตบนหรือล่าง
trait Existential {
type Parameter <: Interface
}
ประเภทสากลที่ถูก จำกัด เท่ากับประเภทที่มีอยู่ดังในตัวอย่างต่อไปนี้
trait Existential[Parameter <: Interface]
ไซต์ใช้งานใด ๆ สามารถใช้งานได้Interface
เนื่องจากExistential
ต้องระบุประเภทย่อยที่สามารถtype Parameter
ใช้งานInterface
ได้ทันที
กรณีเลวของประเภทของการดำรงอยู่ใน Scala เป็นประเภทนามธรรมซึ่งไม่เคยเรียกจึงไม่จำเป็นต้องกำหนดโดยชนิดย่อยใด ๆ สิ่งนี้มีประสิทธิภาพอย่างย่อList[_]
ใน ScalaและList<?>
Java
คำตอบของฉันได้รับแรงบันดาลใจจากข้อเสนอของ Martin Odersky ในการรวมเอานามธรรมและสิ่งมีชีวิตเข้าด้วยกัน ประกอบสไลด์เครื่องช่วยทำความเข้าใจ
∀x.f(x)
ทึบแสงสำหรับฟังก์ชั่นการรับของพวกเขาขณะที่ประเภท Existential ∃x.f(x)
, ถูก จำกัด ให้มีคุณสมบัติบางอย่าง โดยปกติแล้วพารามิเตอร์ทั้งหมดเป็น Existential เนื่องจากฟังก์ชั่นของพวกเขาจะจัดการพวกเขาโดยตรง อย่างไรก็ตามพารามิเตอร์ทั่วไปอาจมีประเภทที่เป็นสากลเนื่องจากฟังก์ชั่นจะไม่จัดการกับพวกเขานอกเหนือจากการดำเนินงานทั่วไปขั้นพื้นฐานเช่นการได้รับการอ้างอิงดังต่อไปนี้:∀x.∃array.copy(src:array[x] dst:array[x]){...}
forSome
สำหรับพารามิเตอร์ประเภทการวัดปริมาณที่มีอยู่
การวิจัยเกี่ยวกับประเภทข้อมูลนามธรรมและการซ่อนข้อมูลได้นำประเภทของการดำรงอยู่มาสู่ภาษาการเขียนโปรแกรม การทำให้นามธรรมประเภทข้อมูลซ่อนข้อมูลเกี่ยวกับประเภทนั้นดังนั้นลูกค้าประเภทนั้นจึงไม่สามารถละเมิดได้ สมมติว่าคุณได้รับการอ้างอิงไปยังวัตถุ ... บางภาษาอนุญาตให้คุณส่งการอ้างอิงไปยังการอ้างอิงถึงไบต์และทำทุกอย่างที่คุณต้องการให้กับหน่วยความจำชิ้นนั้น สำหรับวัตถุประสงค์ในการรับประกันพฤติกรรมของโปรแกรมมันมีประโยชน์สำหรับภาษาที่จะบังคับให้คุณทำการอ้างอิงวัตถุโดยวิธีการที่นักออกแบบของวัตถุให้ คุณรู้ว่ามีอยู่จริง แต่ไม่มีอะไรเพิ่มเติม
ดู:
ประเภทนามธรรมมีประเภทที่มีอยู่ MITCHEL และพล็อต
ในขณะที่ฉันเข้าใจว่าเป็นวิธีการทางคณิตศาสตร์ในการอธิบายอินเตอร์เฟส / คลาสนามธรรม
สำหรับ T = ∃X {X a; int f (X); }
สำหรับ C # มันจะแปลเป็นประเภทนามธรรมทั่วไป:
abstract class MyType<T>{
private T a;
public abstract int f(T x);
}
"Existential" เพียงหมายความว่ามีบางประเภทที่ปฏิบัติตามกฎที่กำหนดไว้ที่นี่