GHC Перекрывающиеся случаи при обобщении сложения
Пытаясь обобщить (+)
больше, чем просто Num
s, я написал до Addable
учебный класс:
{-# LANGUAGE FlexibleContexts, FlexibleInstances, UndecidableInstances #-}
class Addable a where
(+) :: Addable a => a -> a -> a
instance Addable [a] where
(+) = (++)
instance Num a => Addable a where
(+) = (Prelude.+)
При попытке добавить (объединить) списки, GHC жалуется на перекрывающиеся экземпляры:
*Test> "abc" + "defghi"
<interactive>:84:7:
Overlapping instances for Addable [Char] arising from a use of `+'
Matching instances:
instance Num a => Addable a -- Defined at Utils.hs:23:10
instance Addable [a] -- Defined at Utils.hs:20:10
In the expression: "abc" + "defghi"
In an equation for `it': it = "abc" + "defghi"
Я знаю, что GHC игнорирует контекст при выборе экземпляров классов типов, поэтому стараюсь выбирать между Addable [a]
а также Addable a
это действительно проблема. Однако я ожидаю, что GHC выберет первое определение, поскольку оно более конкретное. Почему этого не происходит?
Кроме того, есть ли элегантный способ решения этой проблемы? Или я иду на это с неправильной точки зрения?
1 ответ
Вам нужно включить перекрывающиеся экземпляры, чтобы фактически использовать их. В более старых версиях компилятора вы могли сделать это для каждого модуля с помощью OverlappingInstances
расширение:
{-# LANGUAGE OverlappingInstances #-}
Это работает правильно для этого кода:
λ> :set -XOverlappingInstances
λ> "abc" + "def"
"abcdef"
Однако этот подход не рекомендуется в более новых версиях GHC (по крайней мере, в 8.0):
Misc.hs:1:73-92: warning: …
-XOverlappingInstances is deprecated:
instead use per-instance pragmas OVERLAPPING/OVERLAPPABLE/OVERLAPS
Таким образом, более современный подход состоит в том, чтобы указать это для каждого отдельного экземпляра:
instance {-# OVERLAPPABLE #-} Num a => Addable a where
(+) = (Prelude.+)
Этот стиль прагмы также существует для OVERLAPPING
, OVERLAPS
а также INCOHERENT
, позволяя вам аннотировать конкретные экземпляры с этими свойствами.