อะไรคือความแตกต่างระหว่างลักษณะใน Rust และ typeclasses ใน Haskell


157

ลักษณะใน Rust ดูเหมือนอย่างน้อยที่สุดก็คล้ายกับtypeclassesใน Haskell แต่ฉันเคยเห็นคนเขียนว่ามีความแตกต่างระหว่างพวกเขา ฉันสงสัยว่าความแตกต่างเหล่านี้คืออะไร


8
ฉันไม่รู้เกี่ยวกับสนิมมากนัก แต่บล็อกสะดุดทั่วไปสำหรับเทคโนโลยีที่คล้ายกันในภาษาอื่น ๆ นั้นเป็นประเภทที่สูงกว่า (เช่นสามารถกำหนดลักษณะของพารามิเตอร์ประเภทต่างๆ แต่ไม่ใช่พารามิเตอร์ของมัน?) และรูปแบบความแปรปรวนแบบย้อนกลับ (เช่นชนิดลักษณะสามารถปรากฏในผลลัพธ์ของฟังก์ชันได้ ในข้อโต้แย้ง?) ตัวอย่างของอดีตใน Haskell คือclass Functor f where fmap :: (a -> b) -> (f a -> f b); class Bounded a where maxBound :: aตัวอย่างของหลังเป็น
Daniel Wagner

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

4
@DanielWagner มีความหลากหลายประเภท Return (เช่นstd::default) และลักษณะการจัดเรียงแบบหลายพารามิเตอร์ (รวมถึงอนาล็อกของการพึ่งพาการทำงาน) แม้ว่า AFAIK จะต้องแก้ไขพารามิเตอร์แรกที่ได้รับการยกเว้น อย่างไรก็ตามไม่มี HKT พวกเขาอยู่ในสิ่งที่อยากได้ในอนาคตอันไกล แต่ยังไม่ถึงขอบฟ้า

4
ข้อแตกต่างคือการรักษาอินสแตนซ์ของเด็กกำพร้า สนิมพยายามที่จะมีกฎการเชื่อมโยงกันที่เข้มงวดขึ้นซึ่งสามารถเขียน impl สำหรับลักษณะใหม่ได้ ดูการสนทนานี้สำหรับรายละเอียดเพิ่มเติม (โดยเฉพาะที่นี่ )
Paolo Falabella

1
สนิมรองรับประเภทที่เกี่ยวข้องและข้อ จำกัด ด้านความเท่าเทียมในขณะนี้แม้ว่าจะไม่ได้มีประสิทธิภาพเท่ากับตระกูลประเภทของ Haskell นอกจากนี้ยังมีประเภทอัตถิภาวนิยมทางวัตถุลักษณะ
แลมบ์ดานางฟ้า

คำตอบ:


61

ในระดับพื้นฐานมีความแตกต่างไม่มาก แต่ก็ยังอยู่ที่นั่น

Haskell อธิบายถึงฟังก์ชั่นหรือค่าที่กำหนดในประเภท typeclass เป็น 'methods' เช่นเดียวกับลักษณะที่อธิบายวิธีการ OOP ในวัตถุที่พวกเขาแนบ อย่างไรก็ตาม Haskell เกี่ยวข้องกับสิ่งเหล่านี้แตกต่างกันปฏิบัติกับพวกเขาเป็นค่าส่วนบุคคลมากกว่าการตรึงพวกเขาไปยังวัตถุที่ OOP จะนำไปสู่การทำ นี่คือความแตกต่างระดับพื้นผิวที่เห็นได้ชัดที่สุดที่มี

สิ่งหนึ่งที่ Rust ไม่สามารถทำได้เป็นระยะเวลาหนึ่งนั้นเป็นลักษณะที่พิมพ์ตามลำดับสูงกว่าเช่นความน่าอับอายFunctorและการMonadพิมพ์ผิด

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

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

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

โดยรวมแล้วลักษณะและประเภทของ typeclasses มีความแตกต่างพื้นฐานซึ่งเนื่องจากวิธีการที่พวกเขาโต้ตอบทำให้พวกเขากระทำและดูเหมือนค่อนข้างคล้ายกันในตอนท้าย


†บทความดี ๆ เกี่ยวกับประเภทของ Haskell (รวมถึงตัวพิมพ์ที่สูงกว่า) สามารถพบได้ที่นี่และบทสนิมโดยตัวอย่างเกี่ยวกับลักษณะอาจพบได้ที่นี่


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

ตอนนี้ไอเท็มที่เกี่ยวข้องนั้นถือเป็นไอเดียในหลาย ๆ สถานการณ์ใช่ไหม
Vaelus

@Vaelus คุณพูดถูก - คำตอบนี้ควรได้รับการปรับปรุงเล็กน้อย กำลังแก้ไขในขณะนี้
AJFarmar

19

ฉันคิดว่าคำตอบปัจจุบันมองข้ามความแตกต่างพื้นฐานที่สุดระหว่างลักษณะของสนิมและคลาสประเภท Haskell ความแตกต่างเหล่านี้เกี่ยวข้องกับลักษณะที่สัมพันธ์กับการสร้างภาษาเชิงวัตถุ สำหรับข้อมูลเกี่ยวกับเรื่องนี้ดูหนังสือสนิม

  1. ประกาศลักษณะประเภทสร้างลักษณะนิสัย ซึ่งหมายความว่าคุณสามารถประกาศตัวแปรประเภทดังกล่าว (หรือมากกว่าการอ้างอิงประเภท) นอกจากนี้คุณยังสามารถใช้ประเภทคุณลักษณะเป็นพารามิเตอร์ในฟังก์ชั่นฟิลด์ struct และพารามิเตอร์อินสแตนซ์ได้

    ตัวแปรอ้างอิงคุณลักษณะที่รันไทม์สามารถมีวัตถุประเภทต่าง ๆ ได้ตราบใดที่ประเภทรันไทม์ของวัตถุอ้างอิงนั้นใช้คุณสมบัติ

    // The shape variable might contain a Square or a Circle, 
    // we don't know until runtime
    let shape: &Shape = get_unknown_shape();
    
    // Might contain different kinds of shapes at the same time
    let shapes: Vec<&Shape> = get_shapes();

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

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

  2. วิธีการลักษณะสามารถส่งแบบไดนามิก สิ่งนี้เกี่ยวข้องอย่างยิ่งกับสิ่งต่าง ๆ ที่อธิบายไว้ในส่วนด้านบน

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

    let shape: &Shape = get_unknown_shape();
    
    // This calls a method, which might be Square.area or
    // Circle.area depending on the runtime type of shape
    print!("Area: {}", shape.area());

    อีกครั้งประเภทที่มีอยู่จะใช้สำหรับการนี้ใน Haskell

สรุปแล้ว

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

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


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


2
"คลาสของประเภทไม่สร้างประเภท" - ฉันคิดว่าเป็นการดีที่สุดที่จะเข้าใจdyn Traitว่าเป็นรูปแบบของการพิมพ์ที่มีอยู่เนื่องจากเกี่ยวข้องกับลักษณะ / ประเภทคลาส เราสามารถพิจารณาผู้ประกอบการในขอบเขตที่ยื่นให้พวกเขาชนิดคือdyn dyn : List Bound -> Typeสละความคิดนี้จะ Haskell และมีเรื่องที่เกี่ยวกับ "ดังนั้นคุณไม่สามารถประกาศตัวแปรที่มีชื่อชั้น" เราสามารถทำเช่นนี้โดยทางอ้อมใน data Dyn (c :: * -> Constraint) = forall (t :: Type). c t => D tHaskell: [D True, D "abc", D 42] :: [D Show]มีการกำหนดไว้นี้เราอาจทำงานร่วมกับ
Centril

8

“ ลักษณะ” ของ Rust นั้นคล้ายคลึงกับคลาสประเภทของ Haskell

ความแตกต่างที่สำคัญกับแฮสเคลล์คือการแทรกแซงลักษณะเฉพาะสำหรับการแสดงออกด้วยเครื่องหมายจุดเช่นในรูปแบบ a.foo (b)

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


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