ชื่อของเทคนิคสำหรับอาร์กิวเมนต์ประเภทอนุมานของพารามิเตอร์ชนิดหรือไม่


9

ตั้งค่า:สมมติว่าเรามีประเภทที่เรียกว่าIteratorซึ่งมีพารามิเตอร์ประเภทElement:

interface Iterator<Element> {}

แล้วเรามีอินเตอร์เฟซที่มีวิธีการหนึ่งที่จะกลับIterableIterator

// T has an upper bound of Iterator
interface Iterable<T: Iterator> {
    getIterator(): T
}

ปัญหาของIteratorการเป็นแบบทั่วไปคือเราต้องระบุอาร์กิวเมนต์ประเภท

แนวคิดหนึ่งในการแก้ไขปัญหานี้คือ "อนุมาน" ประเภทของตัววนซ้ำ หลอกรหัสต่อไปนี้เป็นการแสดงออกถึงความคิดที่ว่ามีตัวแปรประเภทElementซึ่งอนุมานว่าจะเป็นอาร์กิวเมนต์ประเภทไปที่Iterator:

interface <Element> Iterable<T: Iterator<Element>> {
    getIterator(): T
}

จากนั้นเราก็ใช้มันในที่นี้:

class Vec<Element> implements Iterable<VecIterator<Element>> {/*...*/}

คำจำกัดความของคำIterableว่าไม่ใช้Elementที่อื่นในคำจำกัดความของมัน แต่กรณีใช้งานจริงของฉัน ฟังก์ชั่นบางอย่างที่ใช้Iterableยังจำเป็นต้องสามารถ จำกัด พารามิเตอร์ของตนเพื่อยอมรับIterables ซึ่งคืนค่าตัววนซ้ำบางชนิดเช่นตัววนซ้ำสองทิศทาง


คำถาม:

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

1
คุณสามารถแสดงรหัสที่ไม่ได้รวบรวมที่คุณต้องการรวบรวมได้ไหม? ฉันคิดว่านั่นจะทำให้คำถามชัดเจนขึ้น
Sweeper

1
คุณอาจเลือกหนึ่งภาษา (หรือพูดภาษาที่คุณใช้และความหมายของไวยากรณ์) เห็นได้ชัดว่าไม่ใช่เช่น C # มีข้อมูลมากมายเกี่ยวกับ "การอนุมานประเภท" ที่มีอยู่บนอินเทอร์เน็ต แต่ฉันไม่แน่ใจว่าจะมีการใช้งานที่นี่

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

คำตอบ:


2

ฉันไม่รู้ว่ามีคำเฉพาะสำหรับปัญหานี้หรือไม่ แต่มีวิธีแก้ไขปัญหาทั่วไปสามประเภท:

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

และแน่นอนทางออกเริ่มต้น: สะกดคำพารามิเตอร์เหล่านั้นทั้งหมด

หลีกเลี่ยงประเภทคอนกรีต

คุณได้กำหนดIterableอินเทอร์เฟซเป็น:

interface <Element> Iterable<T: Iterator<Element>> {
    getIterator(): T
}

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

อย่างไรก็ตามถ้าIterator<E>เป็นอินเทอร์เฟซที่จัดส่งแบบไดนามิกดังนั้นไม่จำเป็นต้องทราบประเภทที่เป็นรูปธรรม นี่คือวิธีแก้ปัญหาที่ Java ใช้ อินเตอร์เฟสจะถูกเขียนเป็น:

interface Iterable<Element> {
    getIterator(): Iterator<Element>
}

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

อนุญาตให้ใช้พารามิเตอร์ประเภทตัวยึดตำแหน่ง

Iterableอินเตอร์เฟซที่ไม่จำเป็นต้องรู้เกี่ยวกับประเภทองค์ประกอบดังนั้นมันอาจจะเป็นไปได้ที่จะเขียนนี้เป็น:

interface Iterable<T: Iterator<_>> {
    getIterator(): T
}

โดยที่T: Iterator<_>เป็นการแสดงออกถึงข้อ จำกัด “ T คือตัววนซ้ำใด ๆ โดยไม่คำนึงถึงประเภทองค์ประกอบ” เพิ่มเติมอย่างจริงจังเราสามารถแสดงนี้ว่า“มีอยู่บางชนิดElementเพื่อให้Tเป็นIterator<Element>Elementโดยไม่ต้องทราบชนิดที่เป็นรูปธรรมใด ซึ่งหมายความว่าการแสดงออกประเภทIterator<_>ไม่ได้อธิบายประเภทที่เกิดขึ้นจริงและสามารถใช้เป็นข้อ จำกัด ประเภท

ใช้ตระกูลตระกูล / ประเภทที่เกี่ยวข้อง

เช่นใน C ++ ประเภทอาจมีสมาชิกประเภท std::vector::value_typeนี้เป็นที่นิยมใช้ทั่วห้องสมุดมาตรฐานเช่น สิ่งนี้ไม่ได้แก้ปัญหาพารามิเตอร์ประเภทในทุกสถานการณ์ แต่เนื่องจากประเภทอาจอ้างถึงประเภทอื่น ๆ พารามิเตอร์ประเภทเดี่ยวจึงสามารถอธิบายตระกูลทั้งหมดของประเภทที่เกี่ยวข้องได้

มานิยามกัน:

interface Iterator {
  type ElementType
  fn next(): ElementType
}

interface Iterable {
  type IteratorType: Iterator
  fn getIterator(): IteratorType
}

แล้ว:

class Vec<Element> implement Iterable {
  type IteratorType = VecIterator<Element>
  fn getIterator(): IteratorType { ... }
}

class VecIterator<T> implements Iterator {
  type ElementType = T
  fn next(): ElementType { ... }
}

สิ่งนี้ดูมีความยืดหยุ่นมาก แต่โปรดทราบว่านี่อาจทำให้ยากที่จะแสดงข้อ จำกัด ประเภท เช่นที่เขียนไว้Iterableไม่ได้บังคับประเภทองค์ประกอบตัววนซ้ำใด ๆ และเราอาจต้องการประกาศinterface Iterator<T>แทน และตอนนี้คุณกำลังจัดการกับแคลคูลัสชนิดที่ค่อนข้างซับซ้อน เป็นเรื่องง่ายมากที่จะทำให้ระบบประเภทดังกล่าวไม่สามารถตัดสินใจได้ (หรืออาจเป็นแบบนั้นอยู่แล้ว?)

โปรดทราบว่าประเภทที่เชื่อมโยงนั้นสะดวกสบายมากเป็นค่าเริ่มต้นสำหรับพารามิเตอร์ประเภท เช่นสมมติว่าIterableอินเทอร์เฟซต้องการพารามิเตอร์ชนิดแยกต่างหากสำหรับประเภทองค์ประกอบซึ่งโดยปกติแล้วจะไม่เหมือนกันกับชนิดองค์ประกอบตัววนซ้ำและเรามีพารามิเตอร์ตัวยึดตำแหน่งอาจเป็นไปได้ที่จะพูดว่า:

interface Iterable<T: Iterator<_>, Element = T::Element> {
  ...
}

อย่างไรก็ตามนั่นเป็นเพียงคุณสมบัติการยศาสตร์ภาษาและไม่ทำให้ภาษามีประสิทธิภาพมากขึ้น


ระบบประเภทนั้นยากดังนั้นจึงเป็นเรื่องดีที่จะดูว่าอะไรดีและใช้งานไม่ได้กับภาษาอื่น

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

ระบบประเภทที่น่าสนใจอื่น ๆ ได้แก่ Haskell ที่มีนามสกุลภาษาต่าง ๆ โมดูล / ฟังก์ชั่น OCaml เป็นประเภทตระกูลที่ค่อนข้างธรรมดาโดยไม่รบกวนโดยตรงกับวัตถุหรือประเภทที่กำหนดพารามิเตอร์ Java โดดเด่นสำหรับข้อ จำกัด ในระบบชนิดของมันเช่น generics ที่มีการลบประเภทและไม่มี generics มากกว่าประเภทค่า C # มีลักษณะคล้าย Java แต่สามารถหลีกเลี่ยงข้อ จำกัด เหล่านี้ส่วนใหญ่ในราคาที่เพิ่มความซับซ้อนในการใช้งาน Scala พยายามรวม C--style generics เข้ากับ typeclasses สไตล์ Haskell ด้านบนของแพลตฟอร์ม Java เทมเพลตแบบง่ายที่หลอกลวงของ C ++ นั้นมีการศึกษามาอย่างดี แต่ไม่เหมือนกับการใช้งานทั่วไป

นอกจากนี้ยังควรดูที่ไลบรารีมาตรฐานของภาษาเหล่านี้ (โดยเฉพาะคอลเล็กชันไลบรารีมาตรฐานเช่นรายการหรือตารางแฮช) เพื่อดูว่ารูปแบบใดที่ใช้กันโดยทั่วไป เช่น C ++ มีระบบที่ซับซ้อนของความสามารถในการวนซ้ำที่แตกต่างกันและ Scala เข้ารหัสความสามารถในการเก็บรวบรวมที่ละเอียดเป็นคุณลักษณะ บางครั้งอินเทอร์เฟซไลบรารีมาตรฐานของ Java จะไม่ปลอดภัยเช่นIterator#remove()แต่สามารถใช้คลาสที่ซ้อนกันเป็นชนิดที่เกี่ยวข้อง (เช่นMap.Entry)

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