Использование класса 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 может иметь только один аргумент, и компилятор в основном обрабатывает его так, как если бы его не было, но использует информацию о типе, которую предоставляет конструктор, чтобы отличать ваши списки от произвольных списков при выборе правильного экземпляра.

Другие вопросы по тегам