2012-03-11 6 views
7

Załóżmy, że mam typ danych jakhaskell - jakikolwiek sposób generowania instancji "wyprowadzających" dla typów danych z grubsza-trojak-izomorfią?

data D a = D a a a 

i typeclass

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

Następnie chcę, aby móc napisać

data D a = D a a a deriving C 

i mają że generowanie instancji

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

za pomocą izomorfizm modulo-lazy-oceny,

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

Uwaga. Użycie nowego typu i GeneralizedNewtypeDeriving nie zadziała, jeśli na przykład użytkownik ma data D m = D (m Integer) (m Integer).

Uwaga 2. To pytanie ma znaczenie dla ogólnej ekspresji Haskella - języki takie jak Python mają coś zwanego nazwanymi krotkami, które mogą być używane wszędzie, gdzie używane są krotki; to pytanie pokazuje, gdzie/jak nie wiem jak naśladować to samo w Haskell.

Odpowiedz

14

Możesz to zrobić względnie czysto i wydajnie, używając GHC 7.4: generic programming support. Pomocne może być documentation for GHC.Generics. Oto przykład.

Rozważmy następujący przykład klasy i niektóre przypadki przykładowe:

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) 

Potrzebujemy pragma językowych i importu:

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

import GHC.Generics hiding(C, D) 

Mamy teraz podać kilka przykładów „rodzajowe”. Typy generyczne wszyscy mają parametr phantom x, co sprawia instancji kieruje się trochę bardziej skomplikowana:

-- "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 

Teraz przedefiniować naszą klasę C wykorzystać 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 

Teraz możemy zdefiniować pewne instancji bardzo łatwo:

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

Zaktualizowano moją odpowiedź, aby uniknąć klasy pomocnika; patrz http://article.gmane.org/gmane.comp.lang.haskell.cafe/97079 – reinerp

Powiązane problemy