Почему 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 не делает.
Ответы
Ответ 1
Это действительно произвольное решение не иметь возможности выводить 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
. Ничто действительно не выделяется как естественный путь.
Ответ 2
Вы можете задать то же самое для Num
и некоторых других классов. Это было бы несущественным: все другие стандартные деривации работают для типов данных с несколькими конструкторами.
В качестве замены вы можете использовать newtype, выводящий newtype T = MkT (a,b,c) deriving Monoid
.
Аналогичное расширение: вы можете сделать пустой тип данных экземпляром почти каждого типа.
Предложение deriving
всегда было ad-hoc и неудобной частью Haskell, потому что оно работало только для предопределенных классов. Добавление еще более ad-hoc-расширений осложняло бы язык. Вместо этого GHC недавно получил поддержку генерации.