2013-07-02 24 views
7

ich im Grunde zu sehen bin versucht, wenn ich einen ORM-Framework in Haskell emulieren kann, so dass, wenn ein Benutzer ein Datenbankmodell machen will, sie so etwas wie dieseMit Generisches Deriving mit einem Rekord Haskell

data Car = Car { 
     company :: String, 
     model :: String, 
     year :: Int 
     } deriving (Model) 
tun würde

Wo die Tabelle wäre "Car", und die Spalten wäre die Firma, Modell, Jahr

Um dies innerhalb Haskell zu tun, müssen Sie eine Kombination von Klassen und Generika verwenden, und das ist, wo ich stecken bleibe . Mit diesem Tutorial (http://www.haskell.org/ghc/docs/7.4.1/html/users_guide/generic-programming.html), kam ich mit diesem nach oben (was im Grunde nur wurde das Kopieren und Umbenennen, so kann ich den Code bekommen arbeiten)

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

module Main where 
import GHC.Generics 

class SModel b where 
     s_new :: b -> IO() 

instance SModel Int where 
     s_new s = putStrLn s++":Int" 

instance SModel Integer where 
     s_new s = putStrLn s++":Integer" 

instance SModel String where 
     s_new s = putStrLn s++":String"  

class Model m where 
     new :: m a -> IO() 

instance Model U1 where 
     new U1 = putStrLn "unit" 

instance (Model a, Model b) => Model (a :*: b) where 
     new (a :*: b) = do 
       new a 
       new b 

instance (Model a, Model b) => Model (a :+: b) where 
     new (L1 x) = new x 
     new (R1 x) = new x 

instance (Model a) => Model (M1 i c a) where 
     new (M1 x) = new x 

instance (SModel a) => Model (K1 i a) where 
     new (K1 x) = s_new x 

data Car = Car { 
     company :: String, 
     model :: String, 
     year :: Int 
     } deriving (Model) 

Der obige Code wird der Fehler

Cannot derive well-kinded instance of form `Model (Car ...)' 
     Class `Model' expects an argument of kind `* -> *' 
    In the data declaration for `Car' 

produziert und Ich bin irgendwie stecken an diesem Punkt, ich glaube, ich habe bereits alle erforderlichen Generic-Typen instanziiert, um einen Datensatz

+6

Sie können 'Model' nicht in einer Ableitungsklausel verwenden. Stattdessen sollten Sie 'Generic' ableiten und dann eine Standardinstanz für' SModel' bereitstellen. Weitere Informationen finden Sie im GHC-Benutzerhandbuch unter "Generische Standardeinstellungen". Es gibt auch eine vorläufige verbesserte Dokumentation unter https://github.com/kosmikus/generic-deriving/blob/master/src/Generics/Deriving/Base.hs – kosmikus

+0

Also Haskell hat keine offizielle Unterstützung für die Verwendung von etwas abgeleitet von der Show, eq (und einige der anderen eingebauten Derivate) – mdedetrich

Antwort

7

zu instanziieren Wie in seinem Kommentar kosmikus sagte, können Sie Model nicht direkt ableiten. Zuerst müssen Sie eine ‚Front-End‘ Klasse für Model eine generische Standard bereitstellt, die wie folgt aussehen könnte:

class FModel a where 
    fnew :: a -> IO() 

    default new :: (Generic a, Model (Rep a)) => a -> IO() 
    fnew = new . from 

Dann können Sie einfach tun:

Car = ... deriving Generic 
instance FModel Car 

Und Sie haben die gewünschte Instanz.

+1

Wenn das sogar zu viel Vortex ist (es wird mehr, wenn Sie mehr Klassen haben, die Sie "ableiten" wollen), können Sie TemplateHaskell verwenden, um alle leeren Instanzen zu generieren, obwohl das nicht der Fall wäre Mach es wenn du nur eine Klasse hast. – bennofs