Автоматический вывод Data.Vector.Unbox с синонимами связанных типов
У меня есть тип данных
newtype Zq q = Zq (IntType q)
где 'q' будет экземпляром класса
class Foo a where
type IntType a
и "IntType" - это просто базовое представление (то есть Int, Integral и т. д.), связанное с "q".
Я хочу сделать Zq экземпляром Data.Vector.Unbox. В настоящее время мы вручную выводим Unbox, используя около 50 строк тривиального кода, как показано в ссылке выше. Мы будем делать несколько различных типов "Unbox" в нашем коде, поэтому написание 50 строк для каждого типа не является привлекательным.
Я нашел две альтернативы здесь. Одна альтернатива - использовать этот пакет, который использует Template Haskell для получения экземпляров Unbox. Код TH будет выглядеть так:
derivingUnbox "Zq"
[d| instance (Foo q, U.Unbox (IntType q)) => Unbox' (ZqBasic q) (IntType q) |]
[| \ (Zq x) -> x |]
[| \ x -> Zq x |]
Проблема в том, что я не могу определить экземпляры, используя синонимы связанных типов (или я могу??)
[ Смежный вопрос: почему TypeSynonymInstances, расширение, подразумеваемое FlexibleInstances, не допускает экземпляры синонимов связанных типов? Это какой-то принципиально другой зверь?]
Мое текущее решение этой проблемы - переопределить Zq как
newtype Zq q i = Zq i
а затем добавить ограничение равенства
i~(IntType q)
в каждом случае с участием (Zq q i), что не очень элегантно. Мой (рабочий) Unbox деривация становится
derivingUnbox "Zq"
[d| instance (U.Unbox i, i~IntType q, Foo q) => Unbox' (Zq q i) i |]
[| \ (Zq x) -> x |]
[| \ x -> Zq x |]
Я чувствую, что должен быть в состоянии сделать это, не прибегая к явному раскрытию типа "i". Все, что я сделал, это переместил его из синонима ассоциированного типа в явный параметр с ограничениями равенства. Почему этот "принципиально" другой (и, очевидно, более безопасный) подход? Есть ли какой-то способ избежать добавления параметра типа 'i' и все же получить автоматический вывод Unbox?
Помимо дополнительного параметра типа, у меня возникли проблемы с использованием пакета TH для получения Unbox для (Vector r), то есть я хочу создать Unbox Vector of Unbox Vectors. Моя попытка что-то вроде:
newtype Bar r = Bar (Vector r)
derivingUnbox "Bar"
[d| instance (Unbox r) => Unbox' (Bar r) (Vector r) |]
[| \ (Bar x) -> x |]
[| \ x -> Bar x |]
но я получаю (много) ошибок, таких как:
`basicUnsafeFreeze` is not a (visible) method of class `Data.Vector.Generic.Base.Vector`
Я не уверен, почему он не может найти этот метод, когда он отлично работает для моего типа Zq.
Второй подход, перечисленный выше, использует расширение GeneralizedNewtypeDeriving. Самая большая проблема, с которой я сталкиваюсь при таком подходе, заключается в том, что у меня есть некоторые фактические данные (а не Newtype), которые должны быть Unbox. Тем не менее, просто используя расширение, я должен быть в состоянии написать
newtype Zq q = Zq (IntType q) deriving (Unbox, M.MVector MVector, G.Vector Vector)
или по крайней мере
newtype Zq q i = Zq i deriving (Unbox, M.MVector MVector, G.Vector Vector)
Первое приводит к ошибкам:
No instance for (Unbox (IntType q)) arising from the `deriving` clause of a data type declaration
No instance for (M.MVector MVector (IntType q)) ""
No instance for (G.Vector Vector (IntType q)) ""
а второй дает:
No instance for (M.MVector MVector i) ""
No instance for (G.Vector U.Vector i) ""
Я не уверен, почему он не может получить эти экземпляры, так как пост выше заставляет меня поверить, что это должно быть в состоянии. Возможно, я мог бы избежать использования синонима связанного типа с GeneralizedNewtypeDeriving? (Это все еще (вероятно) не решает мою проблему, когда мне нужно извлечь Unbox для 'данных.)
Спасибо за вашу помощь!
2 ответа
Здесь вы столкнулись с несколькими отдельными проблемами:
TH подход
Да, экземпляры классов для ассоциированных синонимов типов недопустимы
Это правда, что вы не можете определить экземпляры классов для ассоциированных синонимов типов или функций типов, и на это есть веская причина: компилятор не может определить, перекрываются ли они. Например:
type family F a
instance Eq (F Int)
instance Eq (F Bool)
Эти случаи перекрываются? Учитывая приведенный выше исходный код, мы не можем сказать: это зависит от того, как кто-то позже определяет экземпляры для F
, Например, они могли бы определить
type instance F Int = Double
type instance F Bool = Double
а затем два случая Eq
на самом деле будет перекрываться.
Вы столкнулись с проблемой с vector-th-unbox
пакет
Если вы посмотрите на фактическое Unbox
экземпляр, который вы хотите, вы на самом деле не хотите экземпляр для IntType q
; что вы хотите, это просто:
instance (Unbox (IntType q), Foo q) => Unbox (Zq q) where
...
Проблема в том, что vector-th-unbox
пакет заставляет вас использовать поддельный тип-класс Unbox'
сообщить промежуточный тип представления (IntType q
в вашем случае), как удобный способ злоупотребления синтаксисом Template Haskell для передачи типа. И тогда GHC видит, что вы написали Unbox' (Zq q) (IntType q)
и жалуется. Я бы предложил подать ошибку для vector-th-unbox
пакет.
Unbox
за Vector r
Я думаю, что Луи Вассерман покрыл это.
GeneralizedNewtypeDeriving
подход
Конкретная ошибка компиляции заключается в том, что GHC не может вывести соответствующий контекст. Для большинства проблем, похожих на ту, что у вас есть, StandaloneDeriving
Расширение языка решит вашу проблему:
deriving instance Unbox (IntType q) => Unbox (Zq q)
deriving instance Unbox (IntType q) => M.MVector MVector (Zq q)
deriving instance Unbox (IntType q) => G.Vector Vector (Zq q)
Но НЕ делай этого!
В то время как GeneralizedNewtypeDeriving
часто делает именно то, что вы хотите, это сломано некоторыми фундаментальными способами, и Unbox
Экземпляр, который он производит, полностью сломан! Так что придерживайтесь подхода TH после лоббирования Liyang для решения вашей текущей проблемы.
Я изменил синтаксис в 4820b73, поэтому теперь вы можете делать то, что хотите:
derivingUnbox "Complex"
[d| (Unbox a) ⇒ Complex a → (a, a) |]
[| \ (r :+ i) → (r, i) |]
[| \ (r, i) → r :+ i |]
Я также исправил ошибки "xyz не является (видимым) методом…" в fe37976, хотя можно было обойти это с помощью:
import qualified Data.Vector.Generic
import qualified Data.Vector.Generic.Mutable
Сейчас на Hackage. CC: @reinerp