2012-04-02 7 views
7

La idea básica es que tengo un rango de funciones que funcionan en cualquier tipo de una clase particular, pero en tiempo de ejecución el programa debe leer un archivo de configuración y extraer un elemento de uno de los tipos en la clase.Implementando de forma transparente una forma particular de tipado dinámico

Por ejemplo, tengo una clase 'Coefficient', varias instancias de ella y funciones de varios tipos que son polimórficas sobre los tipos de esa clase; en el tiempo de ejecución, un tipo particular de esa clase debe determinarse y transmitirse.


No estoy seguro de cómo abordar esto correctamente; He intentado que componen los tipos 'compuesto', haciendo algo como:

data CompoundCoeff = CompoundInt Int | CompoundDouble Double | ... 

donde int, double, ... son ejemplos de la 'Coeficiente' clase.
Sin embargo, comenzó a convertirse en un gran esfuerzo adaptar todas las funciones involucradas en el código para trabajar con estos tipos de compuestos (y tampoco es una buena solución). Estaría bien si todas las funciones tuvieran el mismo tipo de letra fácil, por ej.

Coefficient a => a -> (stuff not involving a anymore) 

pero desafortunadamente no es el caso.

Otro problema que encontré, es que estoy usando las familias tipo, y tener algo como

class (Monoid (ColourData c), Coordinate (InputData c)) => ColourScheme c where 
    type ColourData c :: * 
    type InputData c :: * 
    colouriseData  :: c -> (ColourData c) -> AlphaColour Double 
    processInput  :: c -> InputData c -> ColourData c 

Esto no quiere pasar por limpiamente si tengo que utilizar algún tipo de compuesto ColourData tipo de datos, como El anterior; en particular, ya no puedo garantizar que la secuencia de datos proporcione un tipo consistente (y no solo diferentes "subtipos" de un tipo compuesto), y (entre otras cosas) tendría que inventar una instancia falsa de Monoid si inventara una compuesto tipo ColourData.

También he mirado en Data.Dynamic, pero de nuevo no veo cómo abordaría adecuadamente los problemas; exactamente los mismos problemas parecen aparecer (bueno, un poco peor incluso, dado que solo hay un tipo Dinámico "genérico" tal como yo lo entiendo).


Pregunta: ¿Cómo puedo implementar tipos de datos dinámicos subordinados a clases particulares, sin tener que volver a escribir todas las funciones que implican estos tipos de datos? Sería mejor si no tuviera que sacrificar ningún tipo de seguridad, pero no soy demasiado optimista.
Se supone que el programa debe leer un archivo de configuración en tiempo de ejecución y se deben aplicar todas las funciones necesarias, polimórficas sobre la clase relevante.

Respuesta

7

La forma tradicional para proporcionar un objeto que garantiza que es una instancia de clase de tipos Foo, pero no ofrece ninguna garantía adicionales, es de esta manera:

{-# LANGUAGE ExistentialTypes #-} 
data SomeFoo = forall a . Foo a => SomeFoo a 

instance Foo SomeFoo where 
    -- all operations just unwrap the SomeFoo straightforwardly 

o, con GADTs, que podrían ser más legible. ..

data SomeFoo where 
    SomeFoo :: Foo a => a -> SomeFoo 
6

Una propuesta sería escribir una sola función de nivel superior que hace todo los toques finales, una vez que haya elegido un tipo:

topLevel :: SomeTypeClass a => a -> IO() 

Su programa se puede escribir algo como esto:

main = do 
    config <- readConfig 
    case config of 
     UseDouble n -> topLevel n 
     UseSymbolic x -> topLevel x 
     UseWidgetFrobnosticator wf -> topLevel wf 
Cuestiones relacionadas