Что происходит под капотом с "выводом" в 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 зарегистрирован так же, как и класс с ручным кодированием, как если бы он был написан в текущем модуле.