Scala ประเภทที่สูงกว่าคืออะไร


275

คุณสามารถค้นหาสิ่งต่อไปนี้บนเว็บ:

  1. ประเภทที่สูงขึ้นชนิด == ตัวสร้างประเภท?

    class AClass[T]{...} // For example, class List[T]

    บางคนบอกว่านี่เป็นประเภทที่สูงกว่าเพราะมันเป็นนามธรรมมากกว่าประเภทซึ่งจะสอดคล้องกับคำนิยาม

    ประเภทที่สูงขึ้นเป็นประเภทที่ใช้ประเภทอื่น ๆ และสร้างประเภทใหม่

    แม้ว่าเหล่านี้เป็นที่รู้จักกันเป็นประเภทคอนสตรัค (ตัวอย่างเช่นในการเขียนโปรแกรมใน Scala )

  2. typeed type ที่สูงขึ้น == type constructor ซึ่งใช้ตัวสร้าง type เป็นพารามิเตอร์ type?

    ในGenericsกระดาษชนิดที่สูงขึ้นคุณสามารถอ่าน

    ... ประเภทที่เป็นนามธรรมมากกว่าประเภทที่เป็นนามธรรมมากกว่าประเภท ('ประเภทที่สูงกว่าประเภท') ... "

    ซึ่งแสดงให้เห็นว่า

    class XClass[M[T]]{...} // or
    
    trait YTrait[N[_]]{...} // e.g. trait Functor[F[_]]
    

    เป็นประเภทที่สูงขึ้น

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


เพิ่ม Functor ของ Landei เป็นตัวอย่าง
Lutz

คำตอบ:


284

ผมขอชดเชยความสับสนนี้ด้วยการขว้างด้วยความสับสน ฉันชอบใช้การเปรียบเทียบกับระดับค่าเพื่ออธิบายสิ่งนี้เนื่องจากผู้คนมักจะคุ้นเคยกับมันมากกว่า

ตัวสร้างประเภทคือชนิดที่คุณสามารถใช้กับอาร์กิวเมนต์ชนิดเพื่อ "สร้าง" ชนิด

ตัวสร้างค่าเป็นค่าที่คุณสามารถนำไปใช้กับอาร์กิวเมนต์ค่าเพื่อ "สร้าง" ค่า

ตัวสร้างมูลค่ามักเรียกว่า "ฟังก์ชั่น" หรือ "เมธอด" "คอนสตรัคเตอร์" เหล่านี้ถูกกล่าวว่าเป็น "polymorphic" (เพราะพวกเขาสามารถใช้เพื่อสร้าง "สิ่ง" ของ "รูปร่าง" ที่แตกต่างกัน) หรือ "abstractions" (เนื่องจากพวกเขามีความเป็นนามธรรมมากกว่าสิ่งที่แตกต่างกันระหว่าง

ในบริบทของ abstraction / polymorphism คำสั่งแรกหมายถึง "การใช้ครั้งเดียว" ของ abstraction: คุณใช้นามธรรมมากกว่าหนึ่งครั้ง แต่ประเภทนั้นไม่สามารถทำให้เป็นนามธรรมได้ Java 5 generics เป็นลำดับแรก

การตีความลำดับแรกของการอธิบายลักษณะนามธรรมข้างต้นคือ:

ตัวสร้างประเภทเป็นประเภทที่คุณสามารถใช้กับอาร์กิวเมนต์ประเภทที่เหมาะสมเพื่อ "สร้าง" ประเภทที่เหมาะสม

ตัวสร้างค่าเป็นค่าที่คุณสามารถใช้กับอาร์กิวเมนต์ค่าที่เหมาะสมเพื่อ "สร้าง" ค่าที่เหมาะสม

เพื่อเน้นว่าไม่มีสิ่งที่เกี่ยวข้อง (ฉันเดาว่าคุณสามารถเรียกสิ่งนี้ว่า "zero-order" แต่ฉันไม่เคยเห็นสิ่งนี้มาใช้เลย) เช่นค่า1หรือประเภทStringเรามักจะพูดบางอย่างว่าเป็นค่าหรือประเภทที่ "เหมาะสม"

ค่าที่เหมาะสมคือ "ใช้งานได้ทันที" ในแง่ที่ว่ามันไม่ได้รอการโต้แย้ง (มันไม่ได้เป็นนามธรรมเหนือพวกเขา) คิดว่ามันเป็นค่าที่คุณสามารถพิมพ์ / ตรวจสอบได้ง่าย ๆ (การทำให้ฟังก์ชั่นเป็นอนุกรมคือการโกง!)

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

"ลำดับที่สูงกว่า" เป็นคำทั่วไปที่หมายถึงการใช้ polymorphism / abstraction ซ้ำ ๆ มันหมายถึงสิ่งเดียวกันสำหรับประเภทและค่า polymorphic เป็นนามธรรมสิ่งที่เป็นนามธรรมที่เป็นนามธรรมสูงกว่าสิ่งที่เป็นนามธรรมมากกว่าบางสิ่งบางอย่าง สำหรับประเภทคำว่า "ระดับสูง" เป็นรุ่นที่มีวัตถุประสงค์พิเศษของ "ลำดับที่สูงกว่า" ทั่วไป

ดังนั้นตัวละครของเราในเวอร์ชันที่สูงกว่าจะกลายเป็น:

ตัวสร้างประเภทคือชนิดที่คุณสามารถใช้กับอาร์กิวเมนต์ชนิด (ชนิดที่เหมาะสมหรือตัวสร้างชนิด) เพื่อ "สร้าง" ชนิดที่เหมาะสม (ตัวสร้าง)

ตัวสร้างค่าเป็นค่าที่คุณสามารถใช้กับอาร์กิวเมนต์ค่า (ค่าที่เหมาะสมหรือตัวสร้างค่า) เพื่อ "สร้าง" ค่าที่เหมาะสม (ตัวสร้าง)

ดังนั้น "ลำดับสูงกว่า" ก็หมายความว่าเมื่อคุณพูดว่า "abstracting over X" คุณหมายถึงมันจริงๆ! สิ่งXที่ถูกทำให้เป็นนามธรรมมากกว่าจะไม่สูญเสีย "สิทธิในการเป็นนามธรรม" ของตัวเอง: มันสามารถสรุปได้ทุกอย่างที่ต้องการ (โดยวิธีการที่ฉันใช้คำกริยา "นามธรรม" ที่นี่เพื่อหมายถึง: ออกจากสิ่งที่ไม่จำเป็นสำหรับการกำหนดค่าหรือประเภทเพื่อที่จะสามารถแตกต่างกัน / ให้โดยผู้ใช้นามธรรมเป็นอาร์กิวเมนต์ .)

นี่คือตัวอย่างบางส่วน (ได้รับแรงบันดาลใจจากคำถามของ Lutz ทางอีเมล) เกี่ยวกับค่าและประเภทที่เหมาะสมลำดับแรกและสูงกว่า:

                   proper    first-order           higher-order

values             10        (x: Int) => x         (f: (Int => Int)) => f(10)
types (classes)    String    List                  Functor
types              String    ({type λ[x] = x})#λ   ({type λ[F[x]] = F[String]})#λ

ที่คลาสที่ใช้ถูกกำหนดเป็น:

class String
class List[T]
class Functor[F[_]]

เพื่อหลีกเลี่ยงการอ้อมผ่านการกำหนดคลาสคุณจำเป็นต้องแสดงฟังก์ชันชนิดไม่ระบุชื่อซึ่งไม่สามารถแสดงได้โดยตรงใน Scala แต่คุณสามารถใช้ประเภทโครงสร้างโดยไม่มีค่าใช้จ่ายทางไวยากรณ์มากเกินไป (the- style เกิดจากhttps://stackoverflow.com / users / 160378 / retronym afaik):

ใน Scala เวอร์ชันสมมุติฐานในอนาคตที่รองรับฟังก์ชั่นประเภทไม่ระบุตัวตนคุณสามารถตัดบรรทัดสุดท้ายจากตัวอย่างเป็น:

types (informally) String    [x] => x              [F[x]] => F[String]) // I repeat, this is not valid Scala, and might never be

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

ป.ล. : จะทำให้เรื่องยุ่งยากมากขึ้น "polymorphic" นั้นมีความกำกวมในวิธีที่แตกต่างกันเนื่องจากบางครั้งชนิด polymorphic หมายถึงประเภทที่มีปริมาณในระดับสากลเช่นForall T, T => Tซึ่งเป็นประเภทที่เหมาะสมเนื่องจากมันจำแนกค่า polymorphic (ใน Scala ค่านี้ เขียนเป็นประเภทโครงสร้าง{def apply[T](x: T): T = x})


1
ดูเพิ่มเติมที่: adriaanm.github.com/research/2010/10/06/…
Adriaan Moors

5
บทความ Adriaan "Type Constructor Polymorphism" ตอนนี้ที่adriaanm.github.com/research/2010/10/06/…
สตีเวนชอว์

ฉันอ่านต่อไปเรื่อย ๆ ว่าเป็นเครือญาติที่สูงขึ้นและจินตนาการถึงวิญญาณเครือญาติ
Janac Meena

110

(คำตอบนี้เป็นความพยายามในการตกแต่งคำตอบ Adriaan Moors โดยข้อมูลกราฟิกและประวัติศาสตร์)

ประเภทที่สูงขึ้นเป็นส่วนหนึ่งของ Scala ตั้งแต่ 2.5

  • ก่อนหน้า Scala ดังเช่นจาวาจนถึงขณะนี้ไม่อนุญาตให้ใช้ตัวสร้างประเภท ("generics" ใน Java) เพื่อใช้เป็นพารามิเตอร์ประเภทของตัวสร้างประเภท เช่น

     trait Monad [M[_]]

    เป็นไปไม่ได้

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

    ประเภทและความจริงที่ได้รับ ** มาจาก "Generics of a Higher Kind" (รูปภาพที่ได้จากGenerics ชนิดที่สูงกว่า )

    ผลที่ตามมาคือคอนสตรัคเตอร์ประเภทนั้น (เช่นList) สามารถใช้งานได้เช่นเดียวกับประเภทอื่น ๆ ในตำแหน่งพารามิเตอร์ประเภทของคอนสตรัคเตอร์ประเภทดังนั้นพวกเขาจึงกลายเป็นประเภทชั้นหนึ่งตั้งแต่ Scala 2.5 (คล้ายกับฟังก์ชั่นซึ่งเป็นค่าที่ชั้นหนึ่งใน Scala)

    ในบริบทของระบบการพิมพ์ที่สนับสนุนชนิดสูงกว่าที่เราสามารถแยกแยะความแตกต่างชนิดที่เหมาะสมประเภทเช่นIntหรือList[Int]จากประเภทลำดับแรกเหมือนListและประเภทชนิดสูงขึ้นเหมือนFunctorหรือMonad(ประเภทประเภทมากกว่านามธรรมซึ่งเป็นนามธรรมมากกว่าชนิด)

    ระบบประเภทของ Java ในด้านอื่น ๆ ไม่สนับสนุนชนิดดังนั้นจึงไม่มีประเภทของ "ประเภทที่สูงกว่า"

    ดังนั้นสิ่งนี้จะต้องเห็นกับพื้นหลังของระบบสนับสนุนประเภท

  • ในกรณีของสกาล่าคุณมักจะเห็นตัวอย่างของตัวสร้างประเภทเช่น

     trait Iterable[A, Container[_]]

    ด้วยพาดหัว "ประเภทที่สูงกว่าประเภท" เช่นในScala สำหรับโปรแกรมเมอร์ทั่วไปส่วน 4.3

    บางครั้งนี่เป็นขีปนาวุธเพราะหลายคนอ้างContainerว่าเป็นประเภทที่สูงกว่าและไม่ใช่Iterableแต่สิ่งที่แม่นยำกว่าคือ

    การใช้Containerเป็นพารามิเตอร์ประเภทคอนสตรัคของ kinded สูง (ขั้นสูง) Iterableพิมพ์ที่นี่


80

ชนิดประเภทสามัญเช่นIntและซึ่งกรณีนี้เป็นค่าเป็นChar *ชนิดของตัวสร้างประเภทนารีที่เหมือนกันMaybeคือ* -> *; คอนสตรัคเตอร์ประเภทไบนารีมีชนิดEither( curried ) ชนิด* -> * -> *และอื่น ๆ คุณสามารถดูประเภทเช่นMaybeและEitherเป็นฟังก์ชันระดับประเภท: พวกเขาใช้ประเภทหนึ่งหรือมากกว่าและกลับประเภท

ฟังก์ชั่นนั้นเป็นคำสั่งที่สูงกว่าถ้ามันมีคำสั่งมากกว่า 1 โดยที่คำสั่งนั้น (อย่างไม่เป็นทางการ) ความลึกในการซ้อนทางซ้ายของลูกศรฟังก์ชั่น:

  • คำสั่ง 0: 1 :: Int
  • คำสั่งซื้อ 1: chr :: Int -> Char
  • การสั่งซื้อสินค้า 2: fix :: (a -> a) -> a,map :: (a -> b) -> [a] -> [b]
  • คำสั่ง 3: ((A -> B) -> C) -> D
  • คำสั่งซื้อ 4: (((A -> B) -> C) -> D) -> E

สั้นเรื่องยาวประเภทที่สูงกว่าเป็นเพียงฟังก์ชั่นการสั่งซื้อระดับสูงประเภท

  • คำสั่ง 0: Int :: *
  • คำสั่งซื้อ 1: Maybe :: * -> *
  • คำสั่ง 2: Functor :: (* -> *) -> Constraint—higher-kinded: แปลงตัวสร้างประเภท unary เป็นข้อ จำกัด ประเภทของคลาส

ตกลงฉันเข้าใจแล้วจะเป็นตัวอย่างใน Scala สำหรับ (* ⇒ *) ⇒ * และ (* ⇒ *) ⇒ (* ⇒ *) ได้อย่างไร Functor ของ Landei จะเข้ากับหมวดหมู่แรกหรือไม่ในหมวดที่สอง?
Lutz

1
@lutz: มันจะอยู่ในประเภทแรก: Functorผลิตประเภทที่เหมาะสม (ดีลักษณะ แต่ความคิดเดียวกัน) จากคอนสตรัคประเภทFunctor[F[_]] F
Jon Purdy

1
@ จอน: โพสต์ที่ชาญฉลาดมากขอบคุณ ตัวแปลงชนิด(* => *) => (* => *)สามารถแสดงใน Scala ได้หรือไม่ หากไม่ใช้ภาษาอื่น
Eugen Labun

@JonPurdy การเปรียบเทียบระหว่างการแกง* ⇒ * ⇒ *จะมีประโยชน์มาก ขอบคุณ!
Lifu Huang

(* ⇒ *) ⇒ (* ⇒ *)สามารถสะกด(* ⇒ *) ⇒ * ⇒ *ได้ มันสามารถแสดงออกใน Scala Foo[F[_], T]เช่น นี้ชนิดของประเภทเช่น (ใน Haskell) ที่newtype Twice f a = Twice (f (f a))(เช่นTwice Maybe IntMaybe (Maybe Int), Twice [] Char[[Char]]) หรือสิ่งที่น่าสนใจมากขึ้นเช่น data Free f a = Pure a | Free (f (Free f a))monad
Jon Purdy

37

ฉันจะบอกว่า: บทคัดย่อชนิดที่พิมพ์ได้สูงกว่าตัวสร้างประเภท เช่นพิจารณา

trait Functor [F[_]] {
   def map[A,B] (fn: A=>B)(fa: F[A]): F[B]
}

นี่Functorคือ "ประเภทที่สูงขึ้น" (ตามที่ใช้ในกระดาษ "Generics of a Kind สูงกว่า" ) มันไม่ได้เป็นตัวสร้างประเภทคอนกรีต ("ลำดับแรก") เช่นList(ซึ่งนามธรรมมากกว่าประเภทที่เหมาะสมเท่านั้น) มันเป็นนามธรรมเหนือทุกประเภท unary ("สั่งซื้อครั้งแรก") ก่อสร้าง (ตามที่แสดงด้วยF[_])

หรือกล่าวอีกนัยหนึ่ง: ใน Java เรามีตัวสร้างชนิดที่ชัดเจน (เช่นList<T>) แต่เราไม่มี "ชนิดที่สูงกว่าประเภท" เนื่องจากเราไม่สามารถสรุปได้ (เช่นเราไม่สามารถเขียนFunctorอินเตอร์เฟสที่กำหนดไว้ด้านบน - อย่างน้อยไม่ตรง )

คำว่า "ลำดับที่สูงขึ้น (ตัวสร้างประเภท) polymorphism" ใช้เพื่ออธิบายระบบที่รองรับ "ประเภทที่สูงกว่าประเภท"


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

3
ใช่. ตามที่คำตอบของจอน (ที่ผมเข้าใจมัน) ในชวาจะเป็นตัวสร้างประเภทเอกมันมีอย่างชัดเจนชนิดList<T> * -> *สิ่งที่ขาดหายไปในคำตอบของจอนคือคุณต้องสามารถสรุปสิ่งที่ "สมบูรณ์" (และไม่ใช่แค่ในวินาทีที่*เป็นจาวา) เพื่อที่จะเรียกมันว่าเป็นประเภทที่สูงขึ้น
Landei

@Landai: Scalaกระดาษสำหรับโปรแกรมเมอร์ทั่วไปในมาตรา 4.3 แนะนำลักษณะ Iterable [A, Container [_]] เป็นชนิดที่สูงกว่า (แม้ว่าจะไม่ชัดเจนถ้า Iterator หรือ Container มีความหมาย) ที่vero690ด้านอื่น ๆใน ส่วนที่ 2.3.1 ใช้คำว่าตัวสร้างประเภทที่สูงกว่าคำสำหรับ someting like (* -> *) -> * (ตัวดำเนินการชนิดที่กำหนดพารามิเตอร์ด้วยตัวสร้างประเภทคำสั่งที่สูงกว่า) ซึ่งมีลักษณะคล้ายกับ Iterator หรือคุณลักษณะ Functor ของคุณ
Lutz

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

2
โดยทั่วไปสิ่งสำคัญคือต้องแยกความแตกต่างคำจำกัดความและการอ้างอิงที่นี่ คำจำกัดความdef succ(x: Int) = x+1แนะนำ "ตัวสร้างมูลค่า" (ดูคำตอบอื่น ๆ ของฉันสำหรับสิ่งที่ฉันหมายถึง) succ(ไม่มีใครจะอ้างถึงค่านี้เป็น succ (x: Int)) โดยการเปรียบเทียบFunctorคือประเภท (แน่นอนสูงกว่า) ที่กำหนดไว้ในคำตอบของคุณ อีกครั้งคุณไม่ควรเรียกมันว่าFunctor[F[_]](คือFอะไรคือ_อะไรพวกเขาไม่ได้อยู่ในขอบเขตโชคไม่ดีที่น้ำตาล syntactic สำหรับอัตถิภาวนิยม muddies น้ำที่นี่โดยทำF[_]สั้น ๆF[T forSome {type T}])
Adriaan Moors

1

Scala REPL ให้:kindคำสั่งว่า

scala> :help kind

:kind [-v] <type>
Displays the kind of a given type.

ตัวอย่างเช่น,

scala> trait Foo[A]
trait Foo

scala> trait Bar[F[_]]
trait Bar

scala> :kind -v Foo
Foo's kind is F[A]
* -> *
This is a type constructor: a 1st-order-kinded type.

scala> :kind -v Foo[Int]
Foo[Int]'s kind is A
*
This is a proper type.

scala> :kind -v Bar
Bar's kind is X[F[A]]
(* -> *) -> *
This is a type constructor that takes type constructor(s): a higher-kinded type.

scala> :kind -v Bar[Foo]
Bar[Foo]'s kind is A
*
This is a proper type.

:helpให้คำจำกัดความที่ชัดเจนดังนั้นฉันคิดว่ามันเป็นมูลค่าการโพสต์ได้ที่นี่ในสิ่งทั้งปวง (Scala 2.13.2)

scala> :help kind

:kind [-v] <type>
Displays the kind of a given type.

    -v      Displays verbose info.

"Kind" is a word used to classify types and type constructors
according to their level of abstractness.

Concrete, fully specified types such as `Int` and `Option[Int]`
are called "proper types" and denoted as `A` using Scala
notation, or with the `*` symbol.

    scala> :kind Option[Int]
    Option[Int]'s kind is A

In the above, `Option` is an example of a first-order type
constructor, which is denoted as `F[A]` using Scala notation, or
* -> * using the star notation. `:kind` also includes variance
information in its output, so if we ask for the kind of `Option`,
we actually see `F[+A]`:

    scala> :k -v Option
    Option's kind is F[+A]
    * -(+)-> *
    This is a type constructor: a 1st-order-kinded type.

When you have more complicated types, `:kind` can be used to find
out what you need to pass in.

    scala> trait ~>[-F1[_], +F2[_]] {}
    scala> :kind ~>
    ~>'s kind is X[-F1[A1],+F2[A2]]

This shows that `~>` accepts something of `F[A]` kind, such as
`List` or `Vector`. It's an example of a type constructor that
abstracts over type constructors, also known as a higher-order
type constructor or a higher-kinded type.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.