Использование класса Additive из числовой прелюдии приводит к перекрывающимся экземплярам
При попытке определить некоторые математические объекты с помощью числовой прелюдии я столкнулся с проблемой. Класс типов Additive определяет экземпляр
instance Additive.C v => Additive.C [v]
Который я читаю "если v является аддитивным, то [v] слишком" (очевидно, я ошибся здесь). Это реализовано что-то вроде
(+) x y = map (\(a,b) -> a + b) $ zip x y
Так что [1,2,3] + [4,5,6] = [5,7,9], что бесполезно для того, что я хочу сделать. Я предположил, что у меня не будет проблем, так как мой тип v не является аддитивным. К сожалению, я все еще получал ошибку перекрывающихся экземпляров, которая показалась мне очень запутанной Я немного прочитал, и теперь я понимаю, что по какой-то причине Haskell игнорирует все до бита "=>", поэтому я должен был прочитать экземпляр по умолчанию, поскольку "любой список потенциально аддитивен в смысле экземпляра по умолчанию". Я пытался использовать OverlappingInstances, несмотря на то, что это расширение имеет репутацию "опасного", но даже это, похоже, не помогает.
Вот мой тестовый пример.
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE MultiParamTypeClasses,FlexibleInstances #-}
{-# LANGUAGE OverlappingInstances #-} --This doesn't seem to help
import NumericPrelude
import qualified Algebra.Additive as Additive
data Test = Red | Green | Blue deriving Show
instance Additive.C [Test] where
zero = undefined
(+) = undefined
negate = undefined
test = [Red] + [Green] + [Blue]
Выдает ошибку (Обновление: это происходит только в более старых версиях GHC. Версия 7.2.2, кажется, принимает это):
Overlapping instances for Additive.C [Test]
arising from a use of `+'
Matching instances:
instance Additive.C v => Additive.C [v]
-- Defined in Algebra.Additive
instance [overlap ok] Additive.C [Test]
-- Defined at Testcase.hs:10:10-26
In the first argument of `(+)', namely `[Red] + [Green]'
In the expression: [Red] + [Green] + [Blue]
In an equation for `test': test = [Red] + [Green] + [Blue]
Означает ли это, что я не могу использовать списки, потому что я не хочу использовать экземпляр Additive по умолчанию? Что я действительно хочу сделать, так это сказать ghc просто забыть тот экземпляр по умолчанию, это возможно? Если нет, я не уверен, куда идти дальше, кроме удаления списков.
1 ответ
Редактировать: Как упомянул @kosmikus, ваш пример тоже хорошо работает для меня. Я использую GHC 7.4.1.
Вы не можете заставить компилятор забыть экземпляр, так как он импортирован, как только вы импортируете модуль, экземпляр определен в. Обратите внимание, что OverlappingInstances
не говорит компилятору забыть экземпляр, а выбирает наиболее конкретный экземпляр, который доступен.
Чтобы предотвратить наложение экземпляров, вы можете использовать оболочку типов, которая используется для различения произвольных списков от списков, которые вы используете. Например, вы можете определить
data TestList = TestList [Test]
Затем вы можете определить пользовательский экземпляр класса типа для TestList
, В большинстве случаев люди используют синтаксис записей, чтобы определить метод доступа к списку, так как вам нужно обернуть и развернуть списки.
data TestList = TestList { list :: [Test] }
Чтобы снизить стоимость дополнительного конструктора, вы можете использовать newtype
вместо data
,
newtype TestList = TestList { list :: [Test] }
newtype
может иметь только один аргумент, и компилятор в основном обрабатывает его так, как если бы его не было, но использует информацию о типе, которую предоставляет конструктор, чтобы отличать ваши списки от произвольных списков при выборе правильного экземпляра.