ประเภทที่มีอยู่จะไม่ถือว่าเป็นการปฏิบัติที่ไม่ดีในการเขียนโปรแกรมการทำงาน ฉันคิดว่าสิ่งที่ทำให้คุณสะดุดคือหนึ่งในการใช้งานที่มีการอ้างถึงมากที่สุดสำหรับอัตถิภาวนิยมคือAntipattern typeclassซึ่งผู้คนจำนวนมากเชื่อว่าเป็นการฝึกฝนที่ไม่ดี
รูปแบบนี้มักจะถูกตัดออกเป็นคำตอบสำหรับคำถามว่าจะมีรายการองค์ประกอบที่พิมพ์ต่างกันได้อย่างไรซึ่งทั้งหมดนั้นใช้ typeclass เดียวกัน ตัวอย่างเช่นคุณอาจต้องการมีรายการค่าที่มีShow
อินสแตนซ์:
{-# LANGUAGE ExistentialTypes #-}
class Shape s where
area :: s -> Double
newtype Circle = Circle { radius :: Double }
instance Shape Circle where
area (Circle r) = pi * r^2
newtype Square = Square { side :: Double }
area (Square s) = s^2
data AnyShape = forall x. Shape x => AnyShape x
instance Shape AnyShape where
area (AnyShape x) = area x
example :: [AnyShape]
example = [AnyShape (Circle 1.0), AnyShape (Square 1.0)]
ปัญหาเกี่ยวกับรหัสเช่นนี้คือ:
- การดำเนินการที่เป็นประโยชน์เพียงอย่างเดียวที่คุณสามารถทำได้
AnyShape
คือการได้รับพื้นที่ของมัน
- คุณยังต้องใช้ตัว
AnyShape
สร้างเพื่อนำชนิดรูปร่างหนึ่งชนิดมาเป็นAnyShape
ประเภท
ดังนั้นเมื่อปรากฎว่าโค้ดส่วนนั้นไม่ได้สิ่งที่คุณสั้นกว่านี้มา:
class Shape s where
area :: s -> Double
newtype Circle = Circle { radius :: Double }
instance Shape Circle where
area (Circle r) = pi * r^2
newtype Square = Square { side :: Double }
area (Square s) = s^2
example :: [Double]
example = [area (Circle 1.0), area (Square 1.0)]
ในกรณีของคลาสที่มีหลายเมธอดเอฟเฟกต์เดียวกันสามารถทำได้โดยทั่วไปเพียงแค่ใช้การเข้ารหัส "บันทึกวิธีการ" แทนที่จะใช้ typeclass เช่นShape
คุณกำหนดประเภทระเบียนที่ฟิลด์เป็น "วิธี" ของShape
ประเภท และคุณเขียนฟังก์ชันเพื่อแปลงแวดวงและสี่เหลี่ยมของคุณเป็นShape
s
แต่นั่นไม่ได้หมายความว่าประเภทอัตถิภาวนิยมเป็นปัญหา! ตัวอย่างเช่นใน Rust พวกเขามีคุณสมบัติที่เรียกว่าวัตถุลักษณะที่คนมักจะอธิบายว่าเป็นประเภทที่มีอยู่เหนือลักษณะ (รุ่นคลาสของประเภทสนิม) ถ้าประเภทของการดำรงอยู่เป็นปฏิปักษ์ใน Haskell นั่นหมายความว่ารัสเลือกวิธีการแก้ปัญหาที่ไม่ดีหรือไม่? No! แรงจูงใจในโลก Haskell เป็นเรื่องเกี่ยวกับไวยากรณ์และความสะดวกสบายไม่เกี่ยวกับหลักการ
วิธีการทางคณิตศาสตร์ที่มากขึ้นของการวางสิ่งนี้ชี้ให้เห็นว่าAnyShape
ประเภทจากด้านบนและDouble
มีisomorphic - มีลักษณะเป็น "การแปลงแบบไม่สูญเสีย" ระหว่างพวกเขา (ดีประหยัดสำหรับความแม่นยำจุดลอยตัว):
forward :: AnyShape -> Double
forward = area
backward :: Double -> AnyShape
backward x = AnyShape (Square (sqrt x))
ดังนั้นการพูดอย่างเคร่งครัดคุณจะไม่ได้รับหรือสูญเสียพลังใด ๆ โดยการเลือกข้อหนึ่งกับอีกข้อ ซึ่งหมายความว่าตัวเลือกควรขึ้นอยู่กับปัจจัยอื่น ๆ เช่นความง่ายในการใช้งานหรือประสิทธิภาพ
และโปรดทราบว่าประเภทการดำรงอยู่มีการใช้งานอื่นนอกเหนือจากตัวอย่างรายการที่แตกต่างกันนี้ดังนั้นจึงเป็นเรื่องดีที่จะมี ตัวอย่างเช่นST
ประเภทของ Haskell ซึ่งช่วยให้เราสามารถเขียนฟังก์ชั่นที่บริสุทธิ์จากภายนอก แต่ใช้การดำเนินการกลายพันธุ์หน่วยความจำภายในใช้เทคนิคตามประเภทที่มีอยู่เพื่อรับประกันความปลอดภัยในการรวบรวมเวลา
ดังนั้นคำตอบทั่วไปคือไม่มีคำตอบทั่วไป การใช้ประเภทที่มีอยู่นั้นสามารถตัดสินได้ในบริบทเท่านั้นและคำตอบอาจแตกต่างกันไปขึ้นอยู่กับคุณสมบัติและไวยากรณ์ที่มีให้ในภาษาต่าง ๆ