Внедрение Показать вручную
Для реализации по умолчанию Show
требуется изрядное количество шаблонов.Об этом позаботятся show-combinators , сократив необходимый код до самого необходимого:
instance Show ... where
showPrec = flip (\(FuncType i o) -> showCon "FuncType" @| i @| o)
Я думаю, что это решение является самым простым из возможных.Никаких расширений, никакой магии классов под капотом.Простое функциональное программирование.
(Отказ от ответственности: я написал две библиотеки, упомянутые в этом посте.)
С GHC Generics
Существует общая реализация Show
вgeneric-data
: gshowsPrec
( ссылка на источник ).Но он показывает типы, объявленные с синтаксисом записи, как записи.
Повторная реализация реализации
Одним из способов, конечно, является копирование реализации и удаление специальной обработки записей.
{- 1. The usual boilerplate -}
class GShow p f where
gPrecShows :: p (ShowsPrec a) -> f a -> PrecShowS
instance GShow p f => GShow p (M1 D d f) where
gPrecShows p (M1 x) = gPrecShows p x
instance (GShow p f, GShow p g) => GShow p (f :+: g) where
gPrecShows p (L1 x) = gPrecShows p x
gPrecShows p (R1 y) = gPrecShows p y
{- 2. A simplified instance for (M1 C), that shows all constructors
using positional syntax. The body mostly comes from the instance
(GShowC p ('MetaCons s y 'False) f). -}
instance (Constructor c, GShowFields p f) => GShow p (M1 C c f) where
gPrecShows p x = gPrecShowsC p (conName x) (conFixity x) x
where
gPrecShowsC p name fixity (M1 x)
| Infix _ fy <- fixity, k1 : k2 : ks <- fields =
foldl' showApp (showInfix name fy k1 k2) ks
| otherwise = foldl' showApp (showCon cname) fields
where
cname = case fixity of
Prefix -> name
Infix _ _ -> "(" ++ name ++ ")"
fields = gPrecShowsFields p x
Операция с типом
(Раздел назван в честь мой блог , но эта тема намного проще.)
Другой способ - преобразовать общее представление нашего типа впритвориться, что это не объявлено с использованием синтаксиса записи.К счастью, единственное различие заключается в параметре фантомного типа, поэтому преобразование может быть таким простым, как coerce
во время выполнения.
unsetIsRecord ::
Coercible (f p) (UnsetIsRecord f p) => Data f p -> Data (UnsetIsRecord f) p
unsetIsRecord = coerce
-- UnsetIsRecord defined at the end
Data
newtype в основном создаеттип данных вне общего представления (что в некотором смысле противоположно тому, что делает Generic
).Мы можем сопоставить обычно объявленный тип типу Data
, используя toData :: a -> Data (Rep a) p
.
Наконец, мы можем напрямую применить функцию gshowsPrec
из библиотеки generic-data
к выводу unsetIsRecord
.
instance Show T where
showsPrec n = gshowsPrec n . unsetIsRecord . toData
UnsetIsRecord
в идеале должно быть в generic-data
, но, поскольку его еще нет, вот возможная реализация:
type family UnsetIsRecord (f :: * -> *) :: * -> *
type instance UnsetIsRecord (M1 D m f) = M1 D m (UnsetIsRecord f)
type instance UnsetIsRecord (f :+: g) = UnsetIsRecord f :+: UnsetIsRecord g
type instance UnsetIsRecord (M1 C ('MetaCons s y _isRecord) f) = M1 C ('MetaCons s y 'False) f)