Что происходит под капотом с "выводом" в Haskell?

Я только начинаю изучать Хаскелл. Я видел много примеров интро, объясняющих основы типов, но они часто имеют оператор deriving под типом. Вот пример из главы 3 RealWorldHaskell:

data Cartesian2D = Cartesian2D Double Double
                   deriving (Eq, Show)

data Polar2D = Polar2D Double Double
               deriving (Eq, Show)

Они объясняют, как это получается в главе 6, которая помогает вам узнать, как она используется.

Насколько я понимаю, deriving (Show) необходимо сообщить Haskell, как превратить ваш тип в строку. Это имеет смысл на практическом уровне. Я исхожу из земли JavaScript, так что мне легко было бы представить, что это будет реализовано так:

Polar2D.prototype.toString = function(){
  return '[Polar2D]';
};

В Haskell они приводят пример того, как реализовать собственный Show для типа Color, вместо использования deriving.

data Color = Red | Green | Blue
instance Show Color where
  Red = "red"
  Green = "green"
  Blue = "blue"

Это означает, что когда ваш в ghci repl, вы можете сделать:

> show Red
"red"

Но это не объясняет, что на самом деле делает deriving, это все еще волшебство для меня.

Мой вопрос: что происходит под капотом с deriving? Кроме того, есть ли место в GitHub в источнике Haskell, где вы можете увидеть реализацию? Это также может быть полезно.

Ответы

Ответ 1

GHC фактически просто пишет тот же экземпляр, который вы написали вручную, если вы передадите -ddump-deriv в компилятор, вы увидите код, который он генерирует. Например:

Prelude> data Color = Red | Green | Blue deriving (Show)

==================== Derived instances ====================
Derived instances:
  instance Show Color where
    showsPrec _ Red = showString "Red"
    showsPrec _ Green = showString "Green"
    showsPrec _ Blue = showString "Blue"
    showList = showList__ (showsPrec 0)


Generic representation:

  Generated datatypes for meta-information:

  Representation types:

Здесь не так уж много волшебства, Show имеет очень механическую реализацию. Внутри он смотрит на внутреннюю форму конструкторов данных (тип DataConRep в источнике GHC), который он получает от перевода интерфейса AST, а затем просматривает имена, предоставленные во внешнем источнике, и добавляет новый экземпляр Show в терминах эти имена и любые вложенные типы, которые также реализуют Show. Новый автоматически сгенерированный typeclass зарегистрирован так же, как и класс с ручным кодированием, как если бы он был написан в текущем модуле.