2016-09-21 3 views
1

Ich versuche, eine Default Klasse zu erstellen, die automatisch weiß, wie Standardwerte erstellt werden. So las ich das relevan wiki page und mein Problem kommt auf diese: Warum funktioniert das typecheck:Rep-Typ in 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) 

Aber dies bedeutet nicht

{-# 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 

Der Fehler wird:

• 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

Die erste Klasse erwartet, dass seine "Argument" 'f' Art haben' * -> * '(da es verwendet sie als' fa'), aber die Klasse Argument 'a' (von' GDefault') erwartet nur einen Typ (so freundlich '*'), aber Sie füttern es immer noch etwas von Art '* -> *'. – Alec

+0

Es ist sehr unklar, was diese Frage fragt - der Typchecker * hat Ihnen gesagt, warum der letztere Code nicht kompiliert! Auch wenn Ihr "Problem" darauf zurückzuführen ist, sollten Sie vielleicht das eigentliche Problem beschreiben. – user2407038

+0

Ich fühle mich ziemlich dumm, es ist genau das, was Alec vorschlägt ... – fakedrake

Antwort

3

Der Compiler-Fehler ist hier hilfreich, aber nur auf diese nervige Art und Weise, wo er Ihnen genau sagt, was falsch ist, aber nicht warum ist es falsch.

Erwartet einen Typ, aber "Rep a" hat Art "* -> *".

Also hier das Problem ist, dass Rep (eine Art Familie) zwei Argumente benötigt (nennen wir sie a und p, wie in Rep a p); Als Typ-Level-Funktion werden diese beiden Typargumente dem Typ "generic" zugeordnet. Zum Beispiel

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, z.B. Empty, repräsentiert den Typ, aus dem wir generisch sind.

  • p ist ein Dummy-Typ, so dass wir unsere Darstellungsarten für höhere Typen (see Generic1 in the documentation) wiederverwenden können.

So, in dem obigen Beispiel würde Rep Empty p-D1 ('MetaData ...) V1 p vereinfachen.

Wir können normalerweise ignorieren, außer wenn es darum geht, neue Typklassen zu definieren, die Vorteile von Generika nutzen. Wir möchten, dass Muster auf Typen wie D1 ('MetaData ...) V1 p pattern, aber wir brauchen einen Weg, den zusätzlichen Parameter zu behandeln.

Ein Trick ist dann, D1 ('MetaData ...) V1 wie ein höherer Typ (wie ein Funktor) zu behandeln. Dies ist unsere f in GDefault.

class GDefault f where 
    gdef :: f a 

Ja a wird immer dieser blöde Parameter, die wir nie verwenden, aber im Gegenzug für Leitungsrauschen wir die Fähigkeit zur Mustererkennung auf den f in unseren Fällen bekommen. Hier sind vier Instanzen, die für die automatischen generische def Implementierungen für die Produktart ermöglichen (:*: ein angehobenes Tupel ist):

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 

Dies, zusammen mit einigen sinnvollen Standardwerten für den numerischen Turm, lassen Sie uns Datentypen definieren wie data Foo = Foo Int Char Float deriving (Show, Generic) und bewerten show (def :: Foo) bis "Foo 0 0 0.0".

Ihr Code hatte gdef :: a, was die falsche Art ist.Wir wollen gdef :: f a, weil die typeclass auf Typen mit Art * -> * definiert ist, daher die Fehlermeldung.

und Vorteil dieser Hilfsklasse zu nehmen, tun wir viel, wie Sie tat:

class Default a where 
    def :: a 

    default def :: (Generic a, GDefault (Rep a)) => a 
    def = to gdef 

to :: Rep a x -> a eine falsche x einführt, die mit unseren gdef :: f a vereint f ~ Rep a zu produzieren, die x Wegwerfen und genau das, was wir haben es beabsichtigt.

You can see this approach elaborated in the data-default package.