ระบบประเภททั่วไปที่ดี


29

เป็นที่ยอมรับกันโดยทั่วไปว่า Java generics ล้มเหลวในบางวิธีที่สำคัญ การรวมกันของ wildcard และขอบเขตทำให้รหัสบางอย่างไม่สามารถอ่านได้อย่างจริงจัง

อย่างไรก็ตามเมื่อฉันดูภาษาอื่น ๆ ฉันก็ดูเหมือนจะไม่พบระบบแบบทั่วไปที่โปรแกรมเมอร์มีความสุข

ถ้าเราทำสิ่งต่อไปนี้เป็นเป้าหมายการออกแบบของระบบประเภท:

  • สร้างการประกาศประเภทที่อ่านง่ายเสมอ
  • ง่ายต่อการเรียนรู้ (ไม่จำเป็นต้องปัดเรื่องความแปรปรวนร่วมความแปรปรวน ฯลฯ )
  • เพิ่มจำนวนข้อผิดพลาดในการคอมไพล์ให้สูงสุด

มีภาษาใดบ้างที่ทำให้ถูกต้อง? ถ้าฉัน google สิ่งเดียวที่ฉันเห็นคือการร้องเรียนเกี่ยวกับวิธีที่ระบบประเภทดูดในภาษา X. ความซับซ้อนแบบนี้มีอยู่ในการพิมพ์ทั่วไปหรือไม่? เราควรยอมแพ้ในการพยายามตรวจสอบความปลอดภัย 100% ณ เวลารวบรวมหรือไม่?

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

ภาคผนวก: ดังที่กล่าวไว้การรวมกันของการพิมพ์ย่อย / การสืบทอดและข้อมูลทั่วไปเป็นสิ่งที่สร้างความซับซ้อนดังนั้นฉันจึงกำลังมองหาภาษาที่ผสมผสานทั้งสองอย่างและหลีกเลี่ยงการระเบิดของความซับซ้อน


2
คุณหมายถึงeasy-to-read type declarationsอะไร เกณฑ์ที่สามนั้นยังคลุมเครือ: ตัวอย่างเช่นฉันสามารถเปลี่ยนดัชนีอาร์เรย์ออกจากข้อยกเว้นของขอบเขตเป็นข้อผิดพลาดในการรวบรวมเวลาโดยไม่ให้คุณทำดัชนีอาร์เรย์ยกเว้นว่าฉันสามารถคำนวณดัชนีในเวลารวบรวมได้ นอกจากนี้เกณฑ์ที่สองยังกำหนดกฎการพิมพ์ย่อย นั่นไม่ใช่สิ่งเลวร้าย แต่คุณควรระวังสิ่งที่คุณถาม
Doval


9
@gnat นี้แน่นอนที่สุดไม่ได้พูดจาโผงผางกับ Java ฉันเขียนโปรแกรมเฉพาะใน Java ประเด็นของฉันคือว่าเป็นที่ยอมรับกันโดยทั่วไปในชุมชน Javaที่ Generics มีข้อบกพร่อง (ไม่ใช่ความล้มเหลวทั้งหมด แต่อาจเป็นเพียงบางส่วน) ดังนั้นจึงเป็นคำถามเชิงตรรกะที่จะถามว่าควรนำไปปฏิบัติอย่างไร ทำไมพวกเขาถึงทำผิดและคนอื่นทำให้ถูกต้อง? หรือเป็นไปไม่ได้ที่จะเอายาชื่อสามัญไปใช้จริงหรือ?
ปีเตอร์

1
ทุกคนแค่ขโมยจาก C # จะมีการร้องเรียนน้อยลง โดยเฉพาะ Java ที่อยู่ในตำแหน่งที่สามารถติดตามได้โดยการคัดลอก แต่พวกเขาตัดสินใจเลือกวิธีการที่ด้อยกว่า คำถามมากมายที่คณะกรรมการออกแบบ Java ยังคงพูดคุยได้ตัดสินใจแล้วและนำไปใช้ใน C # พวกเขาดูเหมือนจะไม่ได้ดูด้วยซ้ำ
usr

2
@emendendroket: ฉันคิดว่าข้อร้องเรียนที่ใหญ่ที่สุดสองข้อเกี่ยวกับ C # generics คือไม่มีทางที่จะใช้ข้อ จำกัด "supertype" (เช่นFoo<T> where SiameseCat:T) และไม่มีความเป็นไปได้ที่จะมีประเภททั่วไปที่ไม่สามารถเปลี่ยนObjectได้ IMHO, .NET จะได้รับประโยชน์จากประเภทรวมซึ่งคล้ายกับโครงสร้าง แต่ยิ่งไม่มีกระดูก หากKeyValuePair<TKey,TValue>เป็นประเภทดังกล่าวIEnumerable<KeyValuePair<SiameseCat,FordFocus>>จะสามารถส่งไปที่IEnumerable<KeyValuePair<Animal,Vehicle>>ได้ แต่จะไม่สามารถวางประเภทได้
supercat

คำตอบ:


24

ในขณะที่ Generics ได้รับความสำคัญในชุมชนการเขียนโปรแกรมการทำงานมานานหลายทศวรรษการเพิ่ม generics ในภาษาการเขียนโปรแกรมเชิงวัตถุมีความท้าทายที่ไม่ซ้ำกันโดยเฉพาะปฏิสัมพันธ์ของ subtyping และ generics

อย่างไรก็ตามแม้ว่าเราจะมุ่งเน้นไปที่ภาษาการเขียนโปรแกรมเชิงวัตถุและ Java โดยเฉพาะระบบ generics ที่ดีกว่านั้นอาจได้รับการออกแบบ:

  1. ประเภททั่วไปควรยอมรับได้ไม่ว่าจะเป็นประเภทอื่น โดยเฉพาะอย่างยิ่งหากTเป็นพารามิเตอร์ประเภทนิพจน์ต่อไปนี้ควรรวบรวมโดยไม่มีคำเตือน:

    object instanceof T; 
    T t = (T) object;
    T[] array = new T[1];
    

    ใช่สิ่งนี้ต้องการให้มีการแก้ไขข้อมูลทั่วไปเช่นเดียวกับภาษาอื่นทุกประเภท

  2. ความแปรปรวนร่วมและความแปรปรวนของประเภทสามัญควรระบุไว้ใน (หรืออนุมานจาก) การประกาศของมันมากกว่าทุกครั้งที่มีการใช้ประเภททั่วไปดังนั้นเราจึงสามารถเขียน

    Future<Provider<Integer>> s;
    Future<Provider<Number>> o = s; 
    

    ค่อนข้างมากกว่า

    Future<? extends Provider<Integer>> s;
    Future<? extends Provider<? extends Number>> o = s;
    
  3. เนื่องจากประเภททั่วไปอาจใช้เวลานานเราจึงไม่จำเป็นต้องระบุซ้ำซ้อน นั่นคือเราควรจะสามารถเขียน

    Map<String, Map<String, List<LanguageDesigner>>> map;
    for (var e : map.values()) {
        for (var list : e.values()) {
            for (var person : list) {
                greet(person);
            }
        }
    }
    

    ค่อนข้างมากกว่า

    Map<String, Map<String, List<LanguageDesigner>>> map;
    for (Map<String, List<LanguageDesigner>> e : map.values()) {
        for (List<LanguageDesigner> list : e.values()) {
            for (LanguageDesigner person : list) {
                greet(person);
            }
        }
    }
    
  4. ทุกประเภทควรยอมรับว่าเป็นพารามิเตอร์ประเภทไม่ใช่เฉพาะประเภทอ้างอิง (ถ้าเราสามารถมีได้int[]ทำไมเราไม่มีList<int>)

ทั้งหมดนี้เป็นไปได้ใน C #


1
สิ่งนี้จะกำจัดยาสามัญอ้างอิงตนเองหรือไม่? ถ้าฉันต้องการจะบอกว่าวัตถุที่เปรียบเทียบได้สามารถเปรียบเทียบตัวเองกับสิ่งที่เป็นประเภทเดียวกันหรือคลาสย่อยได้? สามารถทำได้หรือไม่ หรือถ้าฉันเขียนวิธีการเรียงลำดับที่ยอมรับรายการที่มีวัตถุที่เปรียบเทียบได้ซึ่งทุกคนต้องเปรียบเทียบกัน Enum เป็นอีกตัวอย่างที่ดี: Enum <E ขยาย Enum <E>> ฉันไม่ได้บอกว่าระบบการพิมพ์ควรจะสามารถทำสิ่งเหล่านี้ได้ฉันแค่อยากรู้ว่า C # จัดการกับสถานการณ์เหล่านี้อย่างไร
ปีเตอร์

1
การอนุมานทั่วไปของ Java 7 และการช่วยเหลืออัตโนมัติของ C ++เกี่ยวกับข้อกังวลเหล่านี้ แต่เป็นน้ำตาลเชิงซ้อนและไม่เปลี่ยนกลไกพื้นฐาน

@Snowman การอนุมานชนิดของ Java มีบางกรณีที่น่าสะพรึงกลัวจริงๆเช่นไม่ทำงานกับคลาสนิรนามและไม่ค้นหาขอบเขตที่เหมาะสมสำหรับ wildcard เมื่อคุณประเมินเมธอดทั่วไปเป็นอาร์กิวเมนต์ของเมธอดทั่วไปอื่น
Doval

@Doval นั่นคือเหตุผลที่ฉันบอกว่ามันช่วยด้วยความกังวล: มันแก้ไขอะไรและไม่ได้อยู่ทุกอย่าง Java generics มีปัญหาจำนวนมาก: ในขณะที่ดีกว่าประเภทดิบพวกเขาจะทำให้ปวดหัวมาก

34

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

ตัดกันว่าด้วยยาชื่อสามัญของ Haskell เป็นต้น พวกเขามีความเพียงพอง่ายๆว่าถ้าคุณใช้อนุมานชนิดคุณสามารถเขียนฟังก์ชั่นทั่วไปที่ถูกต้องโดยอุบัติเหตุ ในความเป็นจริงถ้าคุณระบุชนิดเดียวคอมไพเลอร์มักจะพูดกับตัวเอง "ดีฉันได้รับจะทำให้เรื่องนี้ทั่วไป แต่คุณถามฉันจะทำให้มันเฉพาะสำหรับ ints ดังนั้นสิ่งที่."

เป็นที่ยอมรับกันว่าผู้คนใช้ระบบประเภทของ Haskell ในรูปแบบที่ซับซ้อนและน่าตกใจทำให้มันเป็นสิ่งที่น่าเบื่อสำหรับมือใหม่ทุกคน แต่ระบบการพิมพ์พื้นฐานนั้นสง่างามและน่าชื่นชม


1
ขอบคุณสำหรับคำตอบนี้ บทความนี้เริ่มต้นด้วยบางตัวอย่างโจชัวโบลชของที่ได้รับยาชื่อสามัญที่มีความซับซ้อนมากเกินไป: artima.com/weblogs/viewpost.jsp?thread=222021 นี่เป็นความแตกต่างทางวัฒนธรรมระหว่าง Java และ Haskell หรือไม่ซึ่งการสร้างดังกล่าวจะถือว่าดีใน Haskell หรือมีความแตกต่างอย่างแท้จริงกับระบบประเภทของ Haskell ที่หลีกเลี่ยงสถานการณ์เช่นนี้หรือไม่?
ปีเตอร์

10
@Peter Haskell ไม่มีประเภทย่อยและชอบ Karl กล่าวว่าคอมไพเลอร์สามารถอนุมานชนิดโดยอัตโนมัติรวมถึงข้อ จำกัด เช่น "type aต้องเป็นจำนวนเต็มบางประเภท"
Doval

ในคำอื่น ๆความแปรปรวนร่วมในภาษาเช่น Scala
Paul Draper

14

มีงานวิจัยเล็กน้อยเกี่ยวกับการรวมยาสามัญกับ subtyping เกิดขึ้นเมื่อประมาณ 20 ปีที่แล้ว ภาษาการเขียนโปรแกรม Thor ที่พัฒนาโดยกลุ่มวิจัยของ Barbara Liskov ที่ MIT มีแนวคิดว่า "ที่ไหน" อนุประโยคที่ให้คุณระบุความต้องการของประเภทที่คุณกำลังกำหนดพารามิเตอร์มากกว่า (นี่คล้ายกับสิ่งที่ C ++ พยายามทำกับแนวคิด )

บทความที่อธิบายถึงข้อมูลทั่วไปของ Thor และวิธีที่พวกเขาโต้ตอบกับเชื้อย่อยของ Thor คือ: Day, M; Gruber, R; Liskov, B; ไมเออร์ AC: Subtypes vs. ที่ไหน clauses: บังคับ polamorphism หลากหลายรูปแบบ , ACM Conf สำหรับ Proj Obj-Oriented Prog, Sys, Lang และแอป (OOPSLA-10): 156-158, 1995

ฉันเชื่อว่าพวกเขาก็สร้างงานที่ทำบน Emerald ในช่วงปลายทศวรรษ 1980 (ฉันยังไม่ได้อ่านงาน แต่มีการอ้างอิงคือ: Black, A; Hutchinson, N; Jul, E; Levy, H; Carter, L:แจกจ่ายและบทคัดย่อใน Emerald , _IEEE T. Software Eng., 13 ( 1): 65-76, 1987

ทั้ง Thor และ Emerald เป็น "ภาษาทางวิชาการ" ดังนั้นพวกเขาอาจจะไม่ได้รับการใช้งานมากพอสำหรับคนที่จะเข้าใจจริงๆว่าคำสั่ง (แนวคิด) แก้ปัญหาจริง ๆ ได้หรือไม่ เป็นที่น่าสนใจที่จะอ่านบทความของ Bjarne Stroustrup เกี่ยวกับสาเหตุที่ความพยายามครั้งแรกที่ Concepts ใน C ++ ล้มเหลว: Stroustrup, B: การตัดสินใจ C ++ 0x "ลบแนวคิด" , Dr Dobbs , 22 กรกฏาคม 2009 (ข้อมูลเพิ่มเติมเกี่ยวกับหน้าบ้าน Stroustrup ของ )

ทิศทางที่ผู้คนดูเหมือนจะพยายามก็คือสิ่งที่เรียกว่าลักษณะ ตัวอย่างภาษาการเขียนโปรแกรม Rustของ Mozilla ใช้คุณสมบัติ ตามที่ฉันเข้าใจ (ซึ่งอาจผิดอย่างสิ้นเชิง) การประกาศว่าชั้นเรียนเป็นไปตามลักษณะนิสัยเป็นอย่างมากเช่นการพูดว่าชั้นเรียนใช้อินเทอร์เฟซ แต่คุณกำลังพูดว่า "ประพฤติเหมือน" มากกว่า " ดูเหมือนว่าแอปเปิ้ลใหม่ของการเขียนโปรแกรมภาษาสวิฟท์คือการใช้แนวคิดที่คล้ายกันของโปรโตคอลที่จะระบุข้อ จำกัด ในพารามิเตอร์ยาชื่อสามัญ

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