Производное позиционное шоу - PullRequest
0 голосов
/ 20 декабря 2018

Обратите внимание, как T 5 показывает в

> newtype T = T { getT :: Int } deriving Show
> T 5
T {getT = 5}

Есть ли способ вывести позиционный вариант без синтаксиса записи для типа, который был объявлен с синтаксисом записи?

(кстати T - это всего лишь простой пример для объяснения вопроса, я ищу общий ответ для любого типа, определенного с синтаксисом записи)

Некоторые параметры, которыми я был бы удовлетворен:

  • TH-генерация для него предоставлена ​​библиотекой
  • A Generic деривация на основе (где ручной экземпляр ссылается на существующую функцию)
  • Простой способ / руководство по ручномуреализовать Show экземпляры
  • Любая другая идея, о которой я не думал

Для более сложного примера у меня есть этот рукописный экземпляр :

instance ... where
    showsPrec p (FuncType i o) =
        showParen (p > 0)
        (("FuncType " <>) . showsPrec 1 i . (" " <>) . showsPrec 1 o)

Мне бы хотелось, чтобы ответ был в состоянии избежать этого шаблона.

1 Ответ

0 голосов
/ 20 декабря 2018

Внедрение Показать вручную

Для реализации по умолчанию 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)
...