Rozważmy typ danych z wielu konstruktorów:„dopasowanie Wzór” konstruktorów danych algebraicznych typu
data T = Alpha Int | Beta Int | Gamma Int Int | Delta Int
Chcę napisać funkcję, aby sprawdzić, czy dwie wartości są produkowane z tego samego konstruktora:
sameK (Alpha _) (Alpha _) = True
sameK (Beta _) (Beta _) = True
sameK (Gamma _ _) (Gamma _ _) = True
sameK _ _ = False
Utrzymanie sameK
nie jest zabawne, nie można go łatwo sprawdzić pod kątem poprawności. Na przykład, gdy nowe konstruktory są dodawane do T
, łatwo zapomnieć o aktualizacji sameK
. Pominąłem jedną linię, aby dać przykład:
-- it’s easy to forget:
-- sameK (Delta _) (Delta _) = True
Powstaje pytanie, jak uniknąć boilerplate w sameK
? Lub jak upewnić się, że sprawdza dla wszystkich konstruktorów T
?
Obejście znalazłem jest stosowanie oddzielnych typów danych dla każdego z wykonawców, wynikające Data.Typeable
i deklarując wspólną klasę typu, ale nie podoba mi się to rozwiązanie, ponieważ jest o wiele mniej czytelny i w przeciwnym razie tylko proste algebraiczne typu działa na mnie:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Typeable
class Tlike t where
value :: t -> t
value = id
data Alpha = Alpha Int deriving Typeable
data Beta = Beta Int deriving Typeable
data Gamma = Gamma Int Int deriving Typeable
data Delta = Delta Int deriving Typeable
instance Tlike Alpha
instance Tlike Beta
instance Tlike Gamma
instance Tlike Delta
sameK :: (Tlike t, Typeable t, Tlike t', Typeable t') => t -> t' -> Bool
sameK a b = typeOf a == typeOf b
Dziękuję wszystkim za odpowiedź. Wszystkie twoje odpowiedzi były użyteczne. – sastanin