2016-03-20 10 views
1

Ich versuche herauszufinden, wie generische Herleitung modelliert nach deriveJSON zu tun. I definiert einen einfachen Typ Datensatz Stildaten Konstruktor wie folgt:Zugriff auf Datensatzname und Funktion in Generika

data T = C1 { aInt::Int, aString::String} deriving (Show,Generic) 

Was ich möchte eine generische ableitbar Funktion zu definieren, zu tun ist, die die Daten Konstrukteurs oben nimmt und gibt ein Baumeister mit den Datensatznamen und die Funktionen - nur ein Spielzeug-Code - wollen wir, dass wir es für jeden Datentyp mit einem Rekord Syntax ABuilder generic so verwenden können (wie deriveJSON in Aeson) machen:

{-# LANGUAGE DeriveGeneriC#-} 

import GHC.Generics 

data T = C1 { aInt::Int, aString::String} deriving (Show,Generic) 

-- Some kind of builder output - String here is a stand-in for the 
-- builder 
class ABuilder a where 
    f :: a -> String 

-- Need to get the record field name, and record field function 
-- for each argument, and build string - for anything that is not 
-- a string, we need to add show function - we assume "Show" instance 
-- exists 
instance ABuilder T where 
    f x = ("aInt:" ++ (show . aInt $ x)) ++ "," ++ ("aString:" ++ (aString $ x)) 

Was ich kann nicht herausfinden, wie zu bekommen der Name des Eintrags und die Funktion. Hier ist mein Versuch in ghci 7.10.3. Ich könnte den Namen des Datentyps erhalten, aber ich kann nicht herausfinden, wie man Namen und Funktionen daraus erhält.

$ ghci Test.hs 
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help 
[1 of 1] Compiling Main    (Test.hs, interpreted) 
Ok, modules loaded: Main. 
*Main> datatypeName . from $ (C1 {aInt=1,aString="a"}) 
"T" 
*Main> :t from (C1 {aInt=1,aString="a"}) 
from (C1 {aInt=1,aString="a"}) 
    :: D1 
     Main.D1T 
     (C1 
      Main.C1_0T 
      (S1 Main.S1_0_0T (Rec0 Int) :*: S1 Main.S1_0_1T (Rec0 String))) 
     x 
*Main> 

Ich werde schätzen Hinweise, wie die Satznamen zu erhalten und die Funktion in Generics. Wenn TemplateHaskell ist besser Ansatz für die Definition Generic Instanz ABuilder, werde ich schätzen hören warum. Ich hoffe, bei Generics bei der Kompilierung zu bleiben, wenn die Lösung einfach ist. Ich habe festgestellt, dass AesonTemplateHaskell für deriveJSON Teil verwendet. Deshalb meine Frage über TemplateHaskell oben, um zu sehen, ob es etwas gibt, das ich hier vermisse (ich benutze ghc 7.10.3 und benötige keine Rückwärtskompatibilität mit älteren Versionen).

+0

sehen Wenn Sie 'Generic' ableiten, Die generierten Typen, die Metadaten darstellen (zB 'C1_0T',' S1_0_0T', usw.) haben Instanzen der entsprechenden Klassen ('Datatype',' Constructor', 'Selector'). Diese Instanzen ermöglichen Ihnen den Zugriff auf diese Metadaten. Sie benötigen eine Klasse, um die verschiedenen generischen Konstruktoren zu behandeln, wie in allen Beispielen für Generika. – user2407038

+0

@ user2407038, yep, ich weiß. Meine Frage betrifft den Zugriff auf Datensatznamen und Funktionen. Generische Beispiele, die ich bisher gesehen habe, haben diese Szenarien nicht. – Sal

+1

Es gibt keine Magie - rufen Sie die Methoden der Typklassen auf, dh ['Selector'] (https://hackage.haskell.org/package/base-4.8.2.0/docs/GHC-Generics.html#t:Selector) über die entsprechenden Werte. Genau wie Sie 'datypeName' verwendet haben. Sie haben keinen Code zur Verfügung gestellt, so dass es schwer ist, mehr zu sagen. – user2407038

Antwort

2

Hier ist etwas, was ich peitschte nur das sollte dies, wenn man es die Innereien eines speziellen Konstruktor übergeben:

{-# LANGUAGE DeriveGeneric, TypeOperators, FlexibleContexts, FlexibleInstances #-} 

import GHC.Generics 

data T = C1 { aInt::Int, aString::String} deriving (Show,Generic) 

class AllSelNames x where 
    allSelNames :: x -> [String] 

instance (AllSelNames (a p), AllSelNames (b p)) => AllSelNames ((a :*: b) p) where 
    allSelNames (x :*: y) = allSelNames x ++ allSelNames y 

instance Selector s => AllSelNames (M1 S s f a) where 
    allSelNames x = [selName x] 

Vom repl wir

*Main> let x = unM1 . unM1 $ from (C1 {aInt=1,aString="a"}) 
*Main> allSelNames x 
["aInt","aString"] 
+0

danke, dass du dir Zeit genommen hast, das zu beantworten. Um die Aufzeichnungsfunktion 'aInt' zu extrahieren, würde es ausreichen, eine Musterübereinstimmung auf' M1 {unM1 = K1 {unK1 = 1}} '' zu finden und 'unK1' zu nehmen? – Sal

+0

Das gibt Ihnen den Wert, nicht die Funktion. Denken Sie daran, dass dies funktioniert, wenn Sie atomare Dinge in den Blättern der Aufzeichnungen haben, wenn sie selbst komplexe Strukturen sind, müssen Sie sie zurückbringen, um sie auf normale Daten von ihrem "Rep" zurückzustellen. – sclv