Вопрос по types, haskell, type-extension – Веселье с типами! Разрешение объявлений нескольких экземпляров

5

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

Например, возможноA является полиномиальным классом (с методамиgetCoefficients а такжеmakePolynomial) который может иметь представление в видеSparsePoly илиDensePoly а такжеB класс комплексных чисел (с методамиgetReal, getImag а такжеmakeComplex) который может быть представлен в видеComplexCartesian илиComplexPolar.

Я воспроизвел минимальный пример ниже. У меня два классаA а такжеB каждый из которых имеет реализацию. Я хочу превратить все экземпляры обоих классов в экземплярыNum автоматически (для этого требуетсяFlexibleInstances а такжеUndecidableInstances расширения типа). Это прекрасно работает, когда у меня есть только один изA или жеB, но когда я пытаюсь скомпилировать оба, я получаю следующую ошибку:

<code>Duplicate instance declarations:
  instance [overlap ok] (A a, Num x, Show (a x), Eq (a x)) =>
                        Num (a x)
    -- Defined at test.hs:13:10-56
  instance [overlap ok] (B b, Num x, Show (b x), Eq (b x)) =>
                        Num (b x)
    -- Defined at test.hs:27:10-56
</code>

Я полагаю, что «дублированные объявления экземпляров» сообщение, потому что тип данных может быть сделан как экземпляр обоихA а такжеB, Я хочу иметь возможность дать обещание компилятору, что я этого не сделаю, или, возможно, указать класс по умолчанию для использования в случае, если тип является экземпляром обоих классов.

Есть ли способ сделать это (может быть, другое расширение типа?) Или это то, с чем я застрял?

Вот мой код:

<code>{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-}

class A a where
    fa :: a x -> x
    ga :: x -> a x

data AImpl x = AImpl x deriving (Eq,Show)

instance A AImpl where
    fa (AImpl x) = x
    ga x = AImpl x

instance (A a, Num x, Show (a x), Eq (a x)) => Num (a x) where
    a1 + a2 = ga (fa a1 + fa a2)
    -- other implementations go here


class B b where
    fb :: b x -> x
    gb :: x -> b x

data BImpl x = BImpl x deriving (Eq,Show)

instance B BImpl where
    fb (BImpl x) = x
    gb x = BImpl x

instance (B b, Num x, Show (b x), Eq (b x)) => Num (b x) where
    -- implementations go here
</code>

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

Ваш Ответ

2   ответа
5

Лучшая практика заключается в определении некоторых констант, таких как

plusA, minusA :: (A a, Num x) => a x -> a x -> a x

что делает написаниеNum экземпляры более механические после того, как у вас естьA пример:

instance A Foo where ...
instance Num x => Num (Foo x) where
    (+) = plusA
    (-) = minusA
Error: User Rate Limit Exceeded Chris Taylor
12

I suppose that the 'duplicate instance declarations' message is because a data type could be made an instance of both A and B. I want to be able to make a promise to the compiler that I won't do that, or possibly specify a default class to use in the case that a type is an instance of both classes.

это неверно. Это на самом деле потому, что вы написали два экземпляра,

instance Num (a x)
instance Num (b x)

что компилятор не может отличить друг от друга (см. ссылку из комментария @ hammar, контексты классов не учитываются для разграничения объявлений экземпляров).

Одним из решений является добавление типа свидетеля.

{-# LANGUAGE FlexibleInstances, FlexibleContexts, UndecidableInstances, OverlappingInstances #-}

data AWitness

data AImpl witness x = AImpl x deriving (Eq,Show)

instance A (AImpl AWitness) where
    fa (AImpl x) = x
    ga x = AImpl x

instance (A (a AWitness), Num x, Show (a AWitness x), Eq (a AWitness x)) => Num (a AWitness x) where
    a1 + a2 = ga (fa a1 + fa a2)

Компилятор может использовать типы свидетелей, чтобы различать объявления вашего экземпляра.

Error: User Rate Limit Exceeded Chris Taylor

Похожие вопросы