Обычно в таких случаях я делаю именно то, что вы говорите, что не хотите делать, а именно заключаю функцию в newtype
и предоставляю для этого Show
:
data T1
{ f :: X -> Y
, xs :: [String]
, ys :: [Bool]
}
data T2
{ f :: OpaqueFunction X Y
, xs :: [String]
, ys :: [Bool]
}
deriving (Show)
newtype OpaqueFunction a b = OpaqueFunction (a -> b)
instance Show (OpaqueFunction a b) where
show = const "<function>"
Если вы не хотите этого делать, вы можете вместо этого сделать функцию параметром типа и заменить его при Show
ing типе:
data T3' a
{ f :: a
, xs :: [String]
, ys :: [Bool]
}
deriving (Functor, Show)
newtype T3 = T3 (T3' (X -> Y))
data Opaque = Opaque
instance Show Opaque where
show = const "..."
instance Show T3 where
show (T3 t) = show (Opaque <$ t)
Или я рефакторинг своего типа данных, чтобы получить Show
только для тех частей, которые я хочу сделать Show
способными по умолчанию, и переопределить другие части:
data T4 = T4
{ f :: X -> Y
, xys :: T4' -- Move the other fields into another type.
}
instance Show T4 where
show (T4 f xys) = "T4 <function> " <> show xys
data T4' = T4'
{ xs :: [String]
, ys :: [Bool]
}
deriving (Show) -- Derive ‘Show’ for the showable fields.
Или, если у меня небольшой тип, я буду использовать newtype
вместо data
и выводить Show
через что-то вроде OpaqueFunction
:
{-# LANGUAGE DerivingVia #-}
newtype T5 = T5 (X -> Y, [String], [Bool])
deriving (Show) via (OpaqueFunction X Y, [String], [Bool])
Вы можете использовать пакет iso-deriving
, чтобы сделать это для data
типов, использующих линзы, если вам небезразличны сохранение имен полей/аксессоров записей.
Что касается Eq
(или Ord
), не очень хорошая идея иметь экземпляр, который приравнивает значения, которые можно различить каким-то образом, поскольку один код будет рассматривать их как идентичные, а другой — нет, и теперь вы вынуждены заботиться о стабильности: в некоторых случаях, когда у меня есть a == b
, я должен выбрать a
или b
? Вот почему заменяемость является законом для Eq
: forall x y f. (x == y) ==> (f x == f y)
, если f
является "общедоступной" функцией, которая поддерживает инварианты типа x
и y
(хотя числа с плавающей запятой также нарушают это). Лучшим выбором является что-то вроде T4
выше, имея равенство только для частей типа, которые могут удовлетворять законам, или явно используя сравнение по модулю какой-либо функции на сайтах использования, например, comparing someField
.
person
Jon Purdy
schedule
23.08.2020