Ошибка компилятора здесь полезная, но только тем, что раздражает, когда она сообщает вам, что именно не так, но не почему это неправильно.
Ожидаемый тип, но «Rep a» имеет вид «* -> *».
Таким образом, проблема здесь в том, что Rep
(семейного типа) необходимо два аргумента (назовем их a
и p
, как и в Rep a p
); он как функция уровня уровня сопоставляет эти два аргумента типа в «общий» тип. Например,
data Empty deriving Generic
instance Generic Empty where
type Rep Empty =
D1 ('MetaData "Empty" "Main" "package-name" 'False) V1
-- taken from https://hackage.haskell.org/package/base-4.9.0.0/docs/GHC-Generics.htm
a
, например, Empty
, представляет тип, из которого мы обобщаем.
p
является фиктивным типом, так что мы можем повторно использовать наши типы представления для типов более высокого уровня (see Generic1
in the documentation).
Таким образом, в приведенном выше примере, Rep Empty p
бы упростить D1 ('MetaData ...) V1 p
.
Обычно мы можем игнорировать p
, за исключением случаев, когда речь идет об определении новых моделей, которые используют преимущества дженериков. We хочу для соответствия шаблону по типам типа D1 ('MetaData ...) V1 p
, но нам нужен какой-то способ обработки дополнительного параметра.
Трюк тогда должен лечить D1 ('MetaData ...) V1
как тип более высокого уровня (как функтор). Это наш f
в GDefault
.
class GDefault f where
gdef :: f a
Да a
всегда будет этот глупый параметр, который мы никогда не будем использовать, но в обмен на линии шума мы получаем способность сопоставления с образцом на f
в наших случаях. Вот четыре примера, которые позволяют для автоматических родовых def
реализаций типов продукции (:*:
быть поднят кортежем):
instance GDefault U1 where
gdef = U1
instance Default a => GDefault (K1 i a) where
gdef = K1 def
instance (GDefault a, GDefault b) => GDefault (a :*: b) where
gdef = gdef :*: gdef
instance GDefault a => GDefault (M1 i c a) where
gdef = M1 gdef
Это, наряду с некоторыми разумными значениями по умолчанию для цифровой башни, позволю нам определить типы данных, как data Foo = Foo Int Char Float deriving (Show, Generic)
и оценить show (def :: Foo)
- "Foo 0 0 0.0"
.
Ваш код имел gdef :: a
, который является неправильным видом.Мы хотим, чтобы gdef :: f a
, потому что typeclass определен для типов с видом * -> *
, поэтому сообщение об ошибке.
И чтобы воспользоваться этим вспомогательным классом, мы делаем много, как вы делали:
class Default a where
def :: a
default def :: (Generic a, GDefault (Rep a)) => a
def = to gdef
to :: Rep a x -> a
вводит паразитный x
, который объединяет с нашей gdef :: f a
производить f ~ Rep a
, выбрасывая x
и быть именно то, что мы намеревались.
You can see this approach elaborated in the data-default
package.
Первый класс ожидает, что "аргумент" 'f' иметь вид' * -> * '(так как она использует его как' fa'), но ваш класс аргумент 'a' (из' GDefault') ожидает только тип (так добрый '*'), но вы все еще кормите его чем-то вроде '* -> *'. – Alec
Непонятно, что задает этот вопрос - typechecker * сказал * вам, почему последний код не компилируется! Даже если ваша «проблема сводится к этому», возможно, вам следует описать реальную проблему. – user2407038
Я чувствую себя совершенно немым, это на самом деле то, что предлагает Алек. – fakedrake