2010-10-24 18 views
5

Me gustaría ver si es factible tener una clase de tipo para convertir una cosa en otra y viceversa a partir de una asignación de [(a,b)].Tipo de conversión general clase

Este ejemplo debe ilustrar lo que me gustaría hacer:

data XX = One | Two | Three deriving (Show, Eq) 
data YY = Eno | Owt | Eerht deriving (Show, Eq) 

instance Convert XX YY where 
    mapping = [(One, Eno), (Two, Owt), (Three, Eerht)] 

-- // How can I make this work?: 
main = do print $ (convert One :: YY) -- Want to output: Eno 
      print $ (convert Owt :: XX) -- Want to output: Two 

Aquí es mi puñalada en hacer este trabajo:

{-# LANGUAGE MultiParamTypeClasses #-}  
import Data.Maybe(fromJust) 

lk = flip lookup 
flipPair = uncurry $ flip (,) 

class (Eq a, Eq b) => Convert a b where 

    mapping :: [(a, b)] 
    mapping = error "No mapping defined" 

    convert :: a -> b 
    convert = fromJust . lk mapping 

-- // This won't work: 
instance (Convert a b) => Convert b a where 
    convert = fromJust . lk (map flipPair mapping) 

Es fácil de hacer esto con la definición de dos casos para la la conversión va de cualquier manera pero me gustaría solo tener que declarar una como en el primer ejemplo. ¿Alguna idea de cómo podría hacer esto?


Editar: Por factible Es decir, se puede hacer esto sin superposición casos cualquier otra extensión desagradables?

Respuesta

4

Yo, eh ... casi odio sugerir esto, porque hacer esto es algo horrible, pero ... ¿tu código no funciona como está?

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE UndecidableInstances #-} 
{-# LANGUAGE OverlappingInstances #-} 
import Data.Maybe(fromJust) 

lk x = flip lookup x 
flipPair = uncurry $ flip (,) 

class (Eq a, Eq b) => Convert a b where 
    mapping :: [(a, b)] 
    mapping = error "No mapping defined" 
    convert :: a -> b 
    convert = fromJust . lk mapping 

instance (Convert a b) => Convert b a where 
    convert = fromJust . lk (map flipPair mapping) 

data XX = One | Two | Three deriving (Show, Eq) 
data YY = Eno | Owt | Eerht deriving (Show, Eq) 

instance Convert XX YY where 
    mapping = [(One, Eno), (Two, Owt), (Three, Eerht)] 

main = do print $ (convert One :: YY) 
      print $ (convert Owt :: XX) 

Y:

[1 of 1] Compiling Main    (GeneralConversion.hs, interpreted) 
Ok, modules loaded: Main. 
*Main> main 
Eno 
Two 
*Main> 

no estoy seguro de lo útil una clase de este tipo es, y aplicar todas las renuncias estándar sobre extensiones dudosas, pero que mucho parece funcionar. Ahora, si desea hacer cualquier cosa fancier ... como Convert a a o (Convert a b, Convert b c) => Convert a c ... las cosas pueden ser incómodas.


supongo que también podría dejar algunas reflexiones acerca de por qué dudo de la utilidad de este:

  • Para utilizar la conversión, ambos tipos deben ser conocidos sin ambigüedades; asimismo, la existencia de una conversión depende de ambos tipos. Esto limita la utilidad de la clase para escribir código muy genérico, en comparación con cosas como fromIntegral.

  • El uso de error para manejar las conversiones que faltan, en combinación con lo anterior, significa que cualquier función supuestamente genérico utilizando convert será un pozo hirviente de errores de ejecución a la espera de suceder.

  • Para colmo, la instancia genérica que se utiliza para la asignación inversa es de hecho una instancia universal , solo está oculta por instancias superpuestas y más específicas. Que (Convert a b) en el contexto? Eso permite que el mapeo invertido funcione, pero no lo restringe solo a revertir instancias que están definidas específicamente.

+0

Por simplicidad, podría doblar todos los pragmas de 'IDIOMA' en una lista separada por comas: '{- # LANGUAGE MultiParamTypeClasses, ... # -}'. –

+0

@Antal S-Z: Sí, el asunto de las líneas separadas es solo un hábito mío. Utilizo un editor bastante simple para Haskell y agrego/quito pragmas mientras que refactorizar el código es más fácil si son autónomos. –

+0

Eso es justo; Nunca he tenido más de uno o dos en un proyecto, por lo que refactorizarlos no ha sido un problema importante. Puedo ver la ventaja, sin embargo, ahora que lo pienso. –

Cuestiones relacionadas