Почему GHC не может получить экземпляры для Monoid?

В GHC есть несколько языковых флагов, таких как DeriveFunctor, DeriveDataTypeable и т. Д., Которые позволяют компилятору генерировать производные экземпляры для классов типов, отличных от разрешенных в Haskell 98. Это особенно важно для чего-то вроде Functor, где законы этого класса диктуют очевидный, "естественный" производный экземпляр.

Так почему бы не Monoid? Похоже, что для любого типа данных с одним конструктором данных:

data T = MkT a b c ...

можно было механически создать экземпляр Monoid (извините за псевдокод):

instance (Monoid a, Monoid b, Monoid c, ...) => Monoid T where
  mempty =
    MkT mempty mempty mempty ...
  mappend (MkT a1 b1 c1 ...) (MkT a2 b2 c2 ...) =
    MkT (mappend a1 a2) (mappend b1 b2) (mappend c1 c2) ...

Мне известно, что пакет derive предоставляет это, но мой вопрос конкретно в том, есть ли причина, по которой GHC этого не делает.


person mergeconflict    schedule 22.06.2012    source источник
comment
@sdcvvc: Это похоже на правильный ответ на вопрос. Возможно, вам стоит опубликовать это как таковое?   -  person Tikhon Jelvis    schedule 23.06.2012
comment
Обычно есть только разумный способ создать экземпляр Functor. То же самое с Monoid.   -  person augustss    schedule 23.06.2012
comment
@augustss, можешь немного уточнить? это, наверное, тот ответ, который я ищу.   -  person mergeconflict    schedule 23.06.2012


Ответы (2)


На самом деле решение не иметь возможности наследовать Monoid - произвольное, но моноиды также очень общие, поэтому обычно существует много способов сделать тип моноидом. Вот пример:

data T = A | B | C deriving (Eq, Ord, Enum)

type Mon a = (a, a -> a -> a)

m1, m2, m3, m4 :: Mon T
m1 = (A, max)
m2 = (C, min)
m3 = (A, \ x y -> toEnum $ (fromEnum x + fromEnum y) `rem` 3)
m4 = (B, f4)
f4 A _ = A
f4 B x = x
f4 C _ = C

Здесь показаны четыре разумных способа сделать T моноидом (где Mon содержит единицу и бинарную операцию). Первый - это моноид из максимума, второй - моноид из минимума, третий - моноид из арифметики по модулю 3, а четвертый - моноид, используемый для типа Ordering. Ничто не выделяется лучше естественного.

person augustss    schedule 23.06.2012

Вы можете попросить то же самое для Num и некоторых других классов. Это было бы несущественно: все остальные стандартные производные работают для типов данных с несколькими конструкторами.

В качестве замены вы можете использовать вывод нового типа newtype T = MkT (a,b,c) deriving Monoid.

Аналогичное расширение: вы можете сделать пустой тип данных экземпляром почти любого класса типов.

Предложение deriving всегда было специальной и неудобной частью Haskell, потому что оно работало только для предопределенных классов. Добавление еще большего количества специальных расширений усложнило бы язык. Вместо этого GHC недавно получил поддержку для обобщенного вывода.

person sdcvvc    schedule 23.06.2012
comment
Что ж, каким бы специальным ни был deriving, они продолжили расширять его возможности. Опять же, я знаю, что есть альтернативные механизмы для получения Monoid; вместо этого я конкретно спрашиваю, почему DeriveFunctor (и другие) существует, а DeriveMonoid (и другие) нет. Это просто произвольно? - person mergeconflict; 23.06.2012
comment
В конце концов, это произвольно, но предлагаемое расширение кажется гораздо менее полезным. Мне кажется хорошей особенность языка для обслуживания большинства функторов. Специальная языковая функция для обслуживания моноидов продукта, кажется, не требует добавления. Вам нужно будет подумать о масштабах (если Monoid, то какие еще классы?). Обратите внимание, что полезные моноиды в любом случае часто используют оболочки newtype. Таким образом, вы пишете data T = T (Sum Integer) (Endo Char), чтобы использовать эту форму вывода, которую использовать немного некрасиво (необходимо сопоставить вложенные конструкторы). - person sdcvvc; 23.06.2012
comment
Существует множество полезных моноидов, которые являются типами суммы, но эта реализация не может наследовать ни один из их экземпляров. знак равно - person Louis Wasserman; 23.06.2012
comment
@mergeconflict есть только одно разумное определение Functor для данного типа. Любой другой действительный экземпляр эквивалентен. С Foldable и Traversable история о том, как подразумевается передовая практика в упорядочивании полей, немного менее удовлетворительна. Все они работают с несколькими конструкторами и не имеют огромной кучи альтернативных определений, таких как Monoid. Если вам нужен кортеж моноидов, просто используйте (,,,,). - person Edward KMETT; 27.06.2012