Давайте рассмотрим тип данных с множеством конструкторов:
data T = Alpha Int | Beta Int | Gamma Int Int | Delta Int
Я хочу написать функцию для проверки, генерируются ли два значения с одним и тем же конструктором:
sameK (Alpha _) (Alpha _) = True
sameK (Beta _) (Beta _) = True
sameK (Gamma _ _) (Gamma _ _) = True
sameK _ _ = False
Ведение sameK
не очень весело, его нельзя легко проверить на правильность.Например, когда новые конструкторы добавляются в T
, легко забыть обновить sameK
.Я пропустил одну строку, чтобы привести пример:
-- it’s easy to forget:
-- sameK (Delta _) (Delta _) = True
Вопрос в том, как избежать шаблонов в sameK
?Или как убедиться, что он проверяет все конструкторы T
?
Обходной путь, который я нашел, заключается в использовании отдельных типов данных для каждого из конструкторов, вывод Data.Typeable
и объявлениекласс общего типа, но мне не нравится это решение, потому что оно гораздо менее читабельно, а в остальном у меня работает простой алгебраический тип:
{-# 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