2012-12-24 7 views
7

Ho provato lo Vinyl package, che utilizza i tipi di livello tipo per creare strutture di record con polimorfismo a livello di campo e obiettivi forniti automaticamente. Entrambe queste funzionalità sarebbero molto utili per il mio progetto, in quanto il primo consente strutture di record che sono sottotipi l'una dell'altra senza conflitti di nome e quest'ultima semplifica notevolmente gli aggiornamenti delle strutture nidificate.Esiste un modo per derivare le istanze binarie per i tipi di record di vinile utilizzando Derive e Template Haskell o altrimenti

Il problema si presenta con la serializzazione delle strutture risultanti. Normalmente utilizzo Data.DeriveTH per ricavare automaticamente istanze binarie, ma non sembra in grado di far fronte a queste strutture. Il seguente codice

{-# LANGUAGE DataKinds, TypeOperators #-} 
{-# LANGUAGE FlexibleContexts, NoMonomorphismRestriction #-} 
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-} 
{-# LANGUAGE TemplateHaskell #-} 

import Data.Vinyl 

import Data.Binary 
import Data.DeriveTH 

eID   = Field :: "eID"  ::: Int 
location  = Field :: "location" ::: (Double, Double) 

type Entity = Rec 
    [ "eID"  ::: Int 
    , "location" ::: (Double, Double) 
    ] 

$(derive makeBinary ''Entity) 

risultati in questo errore in GHCI

Exception when trying to run compile-time code: 
    Could not convert Dec to Decl 
TySynD Main.Entity [] (AppT (ConT Data.Vinyl.Rec.Rec) (AppT (AppT PromotedConsT (AppT (AppT (ConT Data.Vinyl.Field.:::) (LitT (StrTyLit "eID"))) (ConT GHC.Types.Int))) (AppT (AppT PromotedConsT (AppT (AppT (ConT Data.Vinyl.Field.:::) (LitT (StrTyLit "location"))) (AppT (AppT (TupleT 2) (ConT GHC.Types.Double)) (ConT GHC.Types.Double)))) PromotedNilT))) 
Language/Haskell/Convert.hs:(37,14)-(40,8): Non-exhaustive patterns in case 

    Code: derive makeBinary ''Entity 
Failed, modules loaded: none. 

Questo sembra essere correlato a questo pezzo di codice nel Derive Convertire modulo:

instance Convert TH.Dec HS.Decl where 
    conv x = case x of 
     DataD cxt n vs con ds -> f DataType cxt n vs con ds 
     NewtypeD cxt n vs con ds -> f NewType cxt n vs [con] ds 
     where 
      f t cxt n vs con ds = DataDecl sl t (c cxt) (c n) (c vs) (c con) [] 

Ora I don' so davvero come leggere Template Haskell, quindi non posso fare molti progressi qui. Mi resi che sto alimentando derivare un tipo sinonimi piuttosto che un tipo di dati e che potrebbe essere rottura, quindi provato avvolgendolo in un newtype:

newtype Entity2 = Entity2 {entity :: Entity} 

$(derive makeBinary ''Entity2) 

che porta a questo errore anche più ottuso:

Exception when trying to run compile-time code: 
    Could not convert Type to Type 
AppT (AppT PromotedConsT (AppT (AppT (ConT Data.Vinyl.Field.:::) (LitT (StrTyLit "eID"))) (ConT GHC.Types.Int))) (AppT (AppT PromotedConsT (AppT (AppT (ConT Data.Vinyl.Field.:::) (LitT (StrTyLit "location"))) (AppT (AppT (TupleT 2) (ConT GHC.Types.Double)) (ConT GHC.Types.Double)))) PromotedNilT) 
Could not convert Type to Type 
AppT PromotedConsT (AppT (AppT (ConT Data.Vinyl.Field.:::) (LitT (StrTyLit "eID"))) (ConT GHC.Types.Int)) 
Could not convert Type to Type 
PromotedConsT 
Language/Haskell/Convert.hs:(71,5)-(80,26): Non-exhaustive patterns in function conv 

Guardando in Convert.hs abbiamo

instance Convert TH.Type HS.Type where 
    conv (ForallT xs cxt t) = TyForall (Just $ c xs) (c cxt) (c t) 
    conv (VarT x) = TyVar $ c x 
    conv (ConT x) | ',' `elem` show x = TyTuple Boxed [] 
        | otherwise = TyCon $ c x 
    conv (AppT (AppT ArrowT x) y) = TyFun (c x) (c y) 
    conv (AppT ListT x) = TyList $ c x 
    conv (TupleT _) = TyTuple Boxed [] 
    conv (AppT x y) = case c x of 
     TyTuple b xs -> TyTuple b $ xs ++ [c y] 
     x -> TyApp x $ c y 

Ora sto cercando di indovinare che cosa sta andando male è che GHC 7.6 ha introdotto una nuova lingua costrutti che il modello Derive Haskell non sta prendendo in considerazione, portando a modelli non esaustivi.

Quindi la mia domanda è, c'è un modo per aggiungere o aggiungere a Derive, o scrivere la mia derivazione da tipi di dischi in vinile, o qualcosa di simile? Sarebbe un peccato se i vantaggi di Vinyl dovessero essere scambiati contro la scrittura a mano di tutte le serializzazioni.

+2

Dovrebbe essere possibile a portata di mano scrivere i casi una volta per tutti i dischi in vinile, simili a come è scritta l'istanza 'Show'. –

+0

Originariamente pensavo che non si potesse fare così, almeno non senza TH, ma ora lo dici che potrei sbagliarmi. Avrò un passaggio ... –

risposta

7

mi aspettavo di incorrere in alcuni problemi con la scrittura dei Binary istanze con tutto il tipo di inganno in corso, ma non potrebbe essere più facile:

instance Binary (Rec '[]) where 
    put RNil = return() 
    get = return RNil 

instance (Binary t, Binary (Rec fs)) => Binary (Rec ((sy ::: t) ': fs)) where 
    put ((_,x) :& xs) = put x >> put xs 
    get = do 
    x <- get 
    xs <- get 
    return ((Field, x) :& xs) 
+3

È incredibile, sono arrivato a metà strada nel tempo che ti ci è voluto, grazie! Quasi ogni giorno incontro una nuova ragione per amare questa lingua e la sua comunità. –

+1

(creatore di vinile qui!) Anche io sono sorpreso di quanto sia stato facile. Bel lavoro, @Sjoerd Visscher! –

Problemi correlati