2012-03-11 13 views
7

Supongamos que tengo un tipo de datos comohaskell: ¿hay alguna forma de generar instancias "derivadas" para tipos de datos aproximadamente tuple-isomórficos?

data D a = D a a a 

y una clase de tipos

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

Entonces, yo quiero ser capaz de escribir

data D a = D a a a deriving C 

y tienen que generar una instancia,

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

mediante el isomorfismo módulo-lazy-evaluación,

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

Nota. Usar un newtype y GeneralizedNewtypeDeriving no funcionará si, por ejemplo, uno tiene data D m = D (m Integer) (m Integer).

Nota 2. Esta pregunta tiene relevancia para la expresividad de Haskell en general: los lenguajes como Python tienen algo llamado tuplas con nombre, que se pueden usar en cualquier lugar donde se usen las tuplas; esta pregunta muestra dónde/cómo no sé cómo emular lo mismo en Haskell.

Respuesta

14

Puede hacerlo de forma relativamente limpia y eficiente utilizando GHC 7.4's generic programming support. El documentation for GHC.Generics puede ser útil. Aquí hay un ejemplo.

Considérese el siguiente ejemplo de clase y algunos casos de ejemplo:

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) 

necesitamos un poco de pragmas lenguaje y las importaciones:

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

import GHC.Generics hiding(C, D) 

Ahora nos dan algunos casos "genéricas". Los tipos genéricos de todo tienen un parámetro fantasma x, lo que hace que la instancia dirige un poco más complicado:

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

ahora Redefinimos nuestra clase C aprovechar 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 

Ahora podemos definir algunos instancias muy fácilmente:

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

Actualizado mi respuesta para evitar la clase de ayuda; ver http://article.gmane.org/gmane.comp.lang.haskell.cafe/97079 – reinerp

Cuestiones relacionadas