2012-03-11 7 views
7

Supponiamo che io sono un tipo di dati comehaskell: un modo per generare istanze "derivative" per tipi di dati isomorfi a tupla approssimativa?

data D a = D a a a 

e typeclass

class C c ... 
instance (C c1, C c2) => C (c1, c2) 

Poi, voglio essere in grado di scrivere

data D a = D a a a deriving C 

e che hanno generato un esempio,

instance C ((a, a), a) => C (D a) 

utilizzando l'isomorfismo modulo-lazy-valutazione,

D a ~ ((a, a), a) 

Note. L'utilizzo di un nuovo tipo e GeneralizedNewtypeDeriving non funzionerà se, ad esempio, uno ha data D m = D (m Integer) (m Integer).

Nota 2. Questa domanda ha rilevanza per l'espressività di Haskell in generale - linguaggi come Python hanno qualcosa chiamato tuple chiamato, che può essere usato ovunque siano usate tuple; questa domanda mostra dove/come non so emulare la stessa cosa in Haskell.

risposta

14

È possibile eseguire questa operazione in modo relativamente pulito ed efficiente utilizzando i codici GHC 7.4 generic programming support. documentation for GHC.Generics potrebbe essere utile. Ecco un esempio.

Si consideri la seguente classe di esempio e alcuni casi di esempio:

class C a where 
    -- | Double all numbers 
    double :: a -> a 

instance C Int where 
    double i = 2 * i 

instance (C a, C b) => C (a, b) where 
    double (a, b) = (double a, double b) 

abbiamo bisogno di alcuni pragma linguistiche e le importazioni:

{-# LANGUAGE TypeOperators, DefaultSignatures, DeriveGeneric, FlexibleContexts, FlexibleInstances #-} 
module Example where 

import GHC.Generics hiding(C, D) 

Diamo ora alcuni "casi generici". I tipi generici hanno tutti un parametro fantasma x, il che rende l'istanza teste un po 'più complicato:

-- "Insert" a normal value into a generic value 
instance C c => C (K1 i c x) where 
    double (K1 c) = K1 (double c) 

-- Ignore meta-information (constructor names, type names, field names) 
instance C (f x) => C (M1 i c f x) where 
    double (M1 f) = M1 (double f) 

-- Tuple-like instance 
instance (C (f x), C (g x)) => C ((f :*: g) x) where 
    double (f :*: g) = double f :*: double g 

Ora ridefinire la nostra classe C per sfruttare GC

class C a where 
    -- | Double all numbers 
    double :: a -> a 

    -- specify the default implementation for double 
    default double :: (Generic a, C (Rep a())) => a -> a 
    double = to0 . double . from0 

-- from, with a more specialised type, to avoid ambiguity 
from0 :: Generic a => a -> Rep a() 
from0 = from 

-- to, with a more specialised type, to avoid ambiguity 
to0 :: Generic a => Rep a() -> a 
to0 = to 

Ora possiamo definire alcuni istanze molto facilmente:

data D a = D a a a deriving Generic 
instance C a => C (D a) 

data D2 m = D2 (m Int) (m Int) deriving Generic 
instance C (D2 D) 
+3

Aggiornamento della mia risposta per evitare la classe di supporto; vedi http://article.gmane.org/gmane.comp.lang.haskell.cafe/97079 – reinerp

Problemi correlati