2013-07-19 5 views
9

Sto cercando di imparare GHC Generics. Dopo aver esaminato diversi esempi, ho voluto provare a creare istanze generiche Functor (ignorando che GHC può ricavarle automaticamente per me). Tuttavia, mi sono reso conto che non ho idea di come lavorare con tipi di dati parametrizzati con Generics, tutti gli esempi che ho visto erano di tipo *. È possibile, e se sì, come? (Sono interessato anche ad altri framework simili, come SYB.)Come costruire istanze di Functor generiche utilizzando GHC.Generics (o altri framework simili)?

risposta

8

Il posto migliore per cercare molte funzioni di esempio utilizzando GHC Generics è lo generic-deriving package. C'è una definizione generica della classe Functor. La copia (leggermente semplificata) da Generics.Deriving.Functor:

class GFunctor' f where 
    gmap' :: (a -> b) -> f a -> f b 

instance GFunctor' U1 where 
    gmap' _ U1 = U1 

instance GFunctor' Par1 where 
    gmap' f (Par1 a) = Par1 (f a) 

instance GFunctor' (K1 i c) where 
    gmap' _ (K1 a) = K1 a 

instance (GFunctor f) => GFunctor' (Rec1 f) where 
    gmap' f (Rec1 a) = Rec1 (gmap f a) 

instance (GFunctor' f) => GFunctor' (M1 i c f) where 
    gmap' f (M1 a) = M1 (gmap' f a) 

instance (GFunctor' f, GFunctor' g) => GFunctor' (f :+: g) where 
    gmap' f (L1 a) = L1 (gmap' f a) 
    gmap' f (R1 a) = R1 (gmap' f a) 

instance (GFunctor' f, GFunctor' g) => GFunctor' (f :*: g) where 
    gmap' f (a :*: b) = gmap' f a :*: gmap' f b 

instance (GFunctor f, GFunctor' g) => GFunctor' (f :.: g) where 
    gmap' f (Comp1 x) = Comp1 (gmap (gmap' f) x) 


class GFunctor f where 
    gmap :: (a -> b) -> f a -> f b 
    default gmap :: (Generic1 f, GFunctor' (Rep1 f)) 
       => (a -> b) -> f a -> f b 
    gmap = gmapdefault 

gmapdefault :: (Generic1 f, GFunctor' (Rep1 f)) 
      => (a -> b) -> f a -> f b 
gmapdefault f = to1 . gmap' f . from1 

Per utilizzare questo su un tipo di dati, è necessario ricavare Generic1 piuttosto che Generic. La differenza chiave della rappresentazione Generic1 consiste nel fatto che utilizza il tipo di dati Par1 che codifica le posizioni dei parametri.

3

Esiste una classe Generic1 per tipi di dati di tipo * -> *. Lavorare con esso è per lo più lo stesso con tipi di dati di tipo *, ad eccezione del parametro Par1. L'ho usato nel mio unfoldable package per esempio.

+0

GHC ricava automaticamente istanze di 'Generic1'? –

+1

@ PetrPudlák Non completamente automatico. Ma con l'estensione di linguaggio 'DeriveGeneric', puoi usare' derivando Generic' e 'derivando Generic1' (dove quest'ultimo funziona solo per i tipi di dati con almeno un parametro, l'ultimo parametro è di tipo' * '). – kosmikus

+0

@kosmikus Grazie. Sfortunatamente per il mio obiettivo mi piacerebbe lavorare con tipi più complessi, quindi probabilmente dovrò usare Template Haskell. –

Problemi correlati