2013-02-10 4 views
4

Представьте, что у нас есть программа Haskell, в которой используется библиотека. Программа предоставляет экземпляр класса TC для типа T из одной из его зависимостей. В следующей версии той же библиотеки авторы библиотеки предоставили еще один экземпляр класса TC для типа T.Два экземпляра одного типа для одного и того же типа

Мы хотим использовать оба экземпляра типа typeclass. Как мы можем это сделать?

P.S. новое решение не будет работать. Оба экземпляра находятся в библиотеках, которые мы не контролируем.

P.P.S. У меня нет примера реального кода. Это теоретический вопрос. Я просто хочу узнать, как классы классов работают с библиотекой.

ответ

5

В Haskell 2010 Report §4.3.2 говорится, что

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

Так что это невозможно в стандартном Haskell.

Мне неизвестно расширение GHC, которое позволит вам сделать это в GHC.

Это (одна?) Причина, по которой сироты (определяющие экземпляр в другом модуле как от типа, так и от типа) обычно считаются плохими.

3

Я могу с уверенностью сказать, что с помощью Cabal вы можете полагаться только на одну версию той же библиотеки.

Хотя возможно, что вы можете хотя бы скопировать исходный код альтернативной версии экземпляра в проект, вы по-прежнему сможете импортировать только один из них для каждого модуля. Если вы импортируете конфликтующие экземпляры в модуль, вы столкнетесь с проблемой нерешимых экземпляров, для которых у вас не будет практического решения.

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

4

В общем случае экземпляры классов нескольких типов для одного и того же типа невозможно. Однако, если тип определен в другом пакете или в старой версии одного и того же пакета, ghc будет считать его другим типом. Таким образом, вы могли бы теоретически определить Foo и экземпляры для него, а foo-1.2 определить Foo и примеры для него, а также использовать их вместе.

Однако на практике это не сработает. Функции будут работать только с одним типом или другим. Когда вы пишете функцию, которая работает на Foo, она будет работать только на одном конкретном Foo, а не на обоих из них. По существу у вас есть два совершенно разных, неопровержимых типа. Было бы неудобно использовать, полностью отделять от того, насколько неудобно строить.

2

Ссылка на различные версии одного и того же пакета на дереве зависимостей теперь невозможна, поскольку приводит к dependency conflict.

Но это будет возможно в будущем, как указано в этом GHC status update video, если они используются только в одной версии в той же библиотеке, я думаю.

3

Если вы хотите улучшить гибкость и композиционную способность, то заново введите типы классов в качестве записи. RankNTypes может быть необходимо.

Например, вот как можно материализовать аппликативном типа класса

{-# LANGUAGE RankNTypes #-} 

data ApplicativeInstance f = ApplicativeInstance 
    { pure :: forall a. a -> f a 
    , amap :: forall a b. (a -> b) -> f a -> f b 
    , ap :: forall a b. f (a -> b) -> f a -> f b 
    } 


listApplicative = ApplicativeInstance 
    { pure = \a -> [a] 
    , amap = map 
    , ap = \fs xs -> case fs of 
     [] -> [] 
     f:fs' -> map f xs ++ ap cartesianListApplicative fs' xs 
    } 


zipListApplicative = ApplicativeInstance 
    { pure = \a -> [a] 
    , amap = map 
    , ap = \fs xs -> case (fs, xs) of 
     ([], _) -> [] 
     (_, []) -> [] 
     (f:fs', x:xs') -> f x : ap zipListApplicative fs' xs' 
    } 

Теперь мы получаем силу указания, какой экземпляр мы хотим. Однако мы теряем способность неявно выбирать экземпляр: теперь выбор должен быть явным.

ghci> ap listApplicative [(+1), (*3)] [1 .. 5] 
[2,3,4,5,6,3,6,9,12,15] 
ghci> ap zip 
zip     zipListApplicative zipWith3 
zip3    zipWith 
ghci> ap zipListApplicative [(+1), (*3)] [1 .. 5] 
[2,6] 

Смотрите также: http://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/

Смежные вопросы