2016-09-21 5 views
1

Я пытаюсь построить класс Default, который автоматически умеет создавать значения по умолчанию. Так что я прочитал relevan wiki page и моя проблема сводится к следующему: Почему это typecheck:Тип Rep в GHC Generics

{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE DeriveGeneriC#-} 
{-# LANGUAGE DefaultSignatures #-} 

import GHC.Generics 

-- From https://wiki.haskell.org/GHC.Generics (sort of) 
class GSerialize f where 
    gput :: f a -> [Int] 
class Serialize a where 
    put :: a -> [Int] 
    default put :: (Generic a, GSerialize (Rep a)) => a -> [Int] 
    put a = gput (from a) 

Но это не

{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE DeriveGeneriC#-} 
{-# LANGUAGE DefaultSignatures #-} 

import GHC.Generics 

class GDefault a where 
    gdef :: a 
class Default a where 
    def :: a 
    default def :: (Generic a, GDefault (Rep a)) => a 
    def = gdef . from 

Ошибка является:

• Expecting one more argument to ‘Rep a’ 
    Expected a type, but ‘Rep a’ has kind ‘* -> *’ 
• In the first argument of ‘GDefault’, namely ‘Rep a’ 
    In the type signature: 
    def :: (Generic a, GDefault (Rep a)) => a 
    In the class declaration for ‘Default’ 
+1

Первый класс ожидает, что "аргумент" 'f' иметь вид' * -> * '(так как она использует его как' fa'), но ваш класс аргумент 'a' (из' GDefault') ожидает только тип (так добрый '*'), но вы все еще кормите его чем-то вроде '* -> *'. – Alec

+0

Непонятно, что задает этот вопрос - typechecker * сказал * вам, почему последний код не компилируется! Даже если ваша «проблема сводится к этому», возможно, вам следует описать реальную проблему. – user2407038

+0

Я чувствую себя совершенно немым, это на самом деле то, что предлагает Алек. – fakedrake

ответ

3

Ошибка компилятора здесь полезная, но только тем, что раздражает, когда она сообщает вам, что именно не так, но не почему это неправильно.

Ожидаемый тип, но «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.