ฉันจะห่ออินสแตนซ์ typeclass ใหม่ได้อย่างไร

ฉันต้องการแสดงแนวคิดเกี่ยวกับขอบเขตบนในโค้ดของฉัน ดังนั้นฉันจึงสร้างสหภาพที่เลือกปฏิบัติ:

data UpperBound a = UpperBound a | DoNotCare deriving (Eq, Read, Show)

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

instance Functor UpperBound where
    fmap _ DoNotCare = DoNotCare
    fmap f (UpperBound x) = UpperBound $ f x

instance Applicative UpperBound where
    pure = UpperBound
    DoNotCare <*> _ = DoNotCare
    _ <*> DoNotCare = DoNotCare
    (UpperBound f) <*> (UpperBound x) = UpperBound $ f x

instance Foldable UpperBound where
    foldr _ s DoNotCare = s
    foldr f s (UpperBound x) = f x s

instance Traversable UpperBound where
    traverse _ DoNotCare = pure DoNotCare
    traverse f (UpperBound x) = fmap UpperBound $ f x

instance Alternative UpperBound where
    empty = DoNotCare
    DoNotCare <|> x = x
    x <|> DoNotCare = x
    x <|> _ = x

instance Monad UpperBound where
    return = pure
    DoNotCare >>= _ = DoNotCare
    (UpperBound x) >>= f = f x

instance MonadPlus UpperBound where
    mzero = empty
    mplus = (<|>)

และฟังก์ชันอรรถประโยชน์เดียว:

isWithinBound :: Ord a => a -> UpperBound a -> Bool
isWithinBound _ DoNotCare = True
isWithinBound x (UpperBound b) = x <= b

อินสแตนซ์ประเภทคลาสมีลักษณะเกือบจะเหมือนกับอินสแตนซ์สำหรับ Maybe (Just x -> UpperBound x, Nothing -> DoNotCare) ดังนั้นดูเหมือนว่าฉันเห็นได้ชัดว่ามีการทำซ้ำโดยไม่จำเป็นที่นี่

ฉันจะ 'ตัด' Maybe และเปลี่ยนเส้นทางการใช้งานอินสแตนซ์ typeclass ไปยังสิ่งนั้นได้อย่างไรและยังคงแสดงฟังก์ชัน isWithinBound ได้อย่างไร


person rexcfnghk    schedule 22.02.2019    source แหล่งที่มา
comment
ไม่แน่ใจทั้งหมดว่าฉันเข้าใจคำถาม แต่คุณใช้ newtype ไม่ได้หรือ หรือแม้แต่ประเภทคำพ้องความหมายหากอินสแตนซ์ของคุณเหมือนกันจริง ๆ (ตามที่ปรากฏ)   -  person Robin Zigmond    schedule 22.02.2019
comment
เกี่ยวกับ isWithinBound การเขียนใหม่เป็นเรื่องเล็กน้อย ดังนั้นจึงต้องใช้ Maybe a แทนเวอร์ชันที่คุณกำหนดเอง   -  person Robin Zigmond    schedule 22.02.2019
comment
@RobinZigmond ฉันหมายถึงฉันยังคงต้องการเปิดเผยเคสเป็น UpperBound/DoNotCare แต่ส่งต่อภายในไปยังการประกาศอินสแตนซ์ไปยังการใช้งาน Maybe เป็นไปได้ไหม?   -  person rexcfnghk    schedule 22.02.2019
comment
อินสแตนซ์ประเภทคลาสมีลักษณะเกือบจะเหมือนกับอินสแตนซ์ของบางทีส่วนใหญ่มาจากไหน   -  person n. 1.8e9-where's-my-share m.    schedule 22.02.2019
comment
@n.m. คุณสามารถ เกือบ ทำการแทนที่ regex ส่วนกลางของ UpperBound ถึง Just และ DoNotCare ถึง None ในการใช้งานอินสแตนซ์ typeclass ทั้งหมด และโค้ดจะยังคงคอมไพล์สำหรับประเภท Maybe   -  person rexcfnghk    schedule 22.02.2019
comment
อินสแตนซ์ Alternative นั้นดูไม่ชัดเจนสำหรับฉัน มันควรจะเป็น monoid สำหรับแต่ละประเภทที่มีอยู่ (โดยมี empty เป็นหน่วยและ <|> เป็นการดำเนินการ) - และโดยเฉพาะอย่างยิ่งมันควรจะมี empty <|> x = x <|> empty = x ซึ่งของคุณไม่มี   -  person Daniel Wagner    schedule 22.02.2019
comment
@DanielWagner คุณพูดถูก ฉันได้เปลี่ยนรหัสเพื่อแก้ไขความสับสนนั้น   -  person rexcfnghk    schedule 03.03.2019


คำตอบ (1)


วิธีที่ง่ายที่สุดคือใช้ส่วนขยาย newtype และ GeneralizedNewtypeDeriving เช่นนี้

{-# LANGUAGE GeneralizedNewtypeDeriving, DeriveTraversable #-}
module UpperBound (UpperBound, isWithinBound) where

import Control.Monad
import Control.Applicative

newtype UpperBound a = UpperBound { unUpperBound :: Maybe a }
  deriving (Functor, Applicative, Foldable, Traversable, Alternative, Monad, MonadPlus)

isWithinBound :: Ord a => a -> UpperBound a -> Bool
isWithinBound x = maybe True ((<=) x) . unUpperBound

ดังนั้นอินสแตนซ์ทั้งหมดที่คุณต้องการจะถูกเปลี่ยนเส้นทางไปยังอินสแตนซ์ของ Maybe โดยอัตโนมัติ (นอกเหนือจาก Traversable ซึ่งได้มาโดยอัตโนมัติโดยใช้ส่วนขยาย DeriveTraversable)

person Karol Samborski    schedule 22.02.2019
comment
ฉันไม่เคยรู้เลยว่า GeneralizedNewtypeDeriving ใช้ไม่ได้กับ Traversable (ฉันไม่เคยใช้มันเลยนอกจาก Functor, Applicative และ Monad) ฉันสงสัยว่าทำไมถึงเป็นเช่นนั้น - person Robin Zigmond; 22.02.2019
comment
@RobinZogmond หาก newtype U x = U (T x) และ Traversable T ดังนั้นการสำรวจจะส่งคืน a f (T x) โดยที่คุณต้องการ f (U x) GHC ต้องการการบังคับขู่เข็ญแบบไม่มีค่าใช้จ่ายระหว่างคนทั้งสอง แต่ไม่มีเลย คุณต้อง fmap U และ GHC จะไม่ทำอย่างนั้นด้วยตัวเอง ตอนนี้ เนื่องจาก Functor f ในทางศีลธรรม f ควรเป็นประเภทบทบาท f การเป็นตัวแทนเสมอ และ ควร มีการบีบบังคับ แต่ 1) Traversable ไม่มีข้อจำกัดนั้น 2) มีบางอย่างที่ "เสียหาย" ” แต่ผู้ทำหน้าที่ที่มีประโยชน์ซึ่งฝ่าฝืนกฎนี้ 3) GND อาจจะไม่สามารถใช้ข้อมูลได้แม้ว่าจะได้รับมาแล้วก็ตาม และ 4) ข้อจำกัดดังกล่าวเป็นส่วนขยาย GHC ที่ใหม่มาก - person HTNW; 23.02.2019