Что именно делает "получение Functor"?

Я пытаюсь выяснить, какие именно правила для deriving Functor в Haskell.

Я видел сообщения о сообщениях об этом, и я видел тестовый код об этом, но я не могу найти официальную документацию о том, что такое правила. Может кто-то прояснить и/или указать мне нужное место?

Ответ 1

Чтобы использовать deriving Functor, вы должны включить прагму языка DeriveFunctor и применить ее к полиморфному типу с ковариантной переменной конечного типа - другими словами, тип, допускающий действительный экземпляр Functor. Затем он получит "очевидный" экземпляр Functor.

В прошлом была некоторая озабоченность тем, что производный экземпляр не так эффективен, как ручной кодированный, хотя я не могу найти этот материал.

Сам алгоритм был, насколько я мог найти, впервые предложенный Twan Van Laarhoven в 2007 году, и активно использует программирование Generic Haskell.

Ответ 2

Код, который фактически делает это дело, к сожалению, немного на волосатой стороне. Я считаю, что в основном потому, что раньше, более простой код иногда приводил к чрезмерному времени компиляции. Twan van Laarhoven придумал текущий код для решения этой проблемы.

Полученный экземпляр Functor всегда делает очевидную вещь. Обычно это нормально, но иногда пропускает возможности. Например, предположим, что я пишу

data Pair a = Pair a a deriving Functor
data Digit a = One a | Two a a deriving Functor
data Queue a =
    Empty
  | Single a
  | Deep !(Digit a) (Queue (Pair a)) !(Digit a) deriving Functor

Это сгенерирует (в GHC 8.2)

instance Functor Queue where
  fmap ...
  x <$ Empty = Empty
  x <$ Single y = Single x
  x <$ Deep pr m sf = Deep (x <$ pr) (fmap (x <$) m) (x <$ sf)

Можно лучше записать этот последний случай вручную:

  x <$ Deep pr m sf = Deep (x <$ pr) (Pair x x <$ m) (x <$ sf)

Вы можете увидеть фактический производный код, используя -ddump-deriv.