2011-12-16 7 views
8

Todavía nuevo a Haskell, he chocado contra un muro con lo siguiente:Tipo con funciones dependiendo de un tipo adicional

que estoy tratando de definir algunas clases de tipos de generalizar un montón de funciones que utilizan la eliminación de Gauss a resolver sistemas lineales de ecuaciones.

Dado un sistema lineal

M x = k 

el tipo a de los elementos m(i,j) \elem M puede ser diferente del tipo b de x y k. Para poder resolver el sistema, a debe ser una instancia de Num y b deben tener los operadores de multiplicación/adición con b, como en el siguiente:

class MixedRing b where 
    (.+.) :: b -> b -> b 
    (.*.) :: (Num a) => b -> a -> b 
    (./.) :: (Num a) => b -> a -> b 

Ahora, incluso en la implementación más trivial de estos operadores, voy a Could not deduce a ~ Int. a is a rigid type variable errores (olvidémonos de ./. que requiere Fractional)

data Wrap = W { get :: Int } 
instance MixedRing Wrap where 
    (.+.) w1 w2 = W $ (get w1) + (get w2) 
    (.*.) w s = W $ ((get w) * s) 

he leído varios tutoriales sobre las clases de tipos, pero no puedo encontrar ningún puntero a lo que realmente va mal.

+5

Ya hay un paquete que define esta clase: http://hackage.haskell.org/packages/archive/vector-space/0.8.0/doc/html/Data-VectorSpace.html Puede echar un vistazo allí. –

+0

@SjoerdVisscher: ¡Gracias! – bbtrb

Respuesta

10

Echemos un vistazo al tipo de implementación que tendría que proporcionar para (.*.) para hacer Wrap una instancia de MixedRing.Sustituyendo Wrap para b en el tipo del método produce

(.*.) :: Num a => Wrap -> a -> Wrap 

Como Wrap es isomorfo a Int y para no tener que pensar en envolver y desenvolver con Wrap y get, reduzcamos nuestro objetivo de encontrar una implementación de

(.*.) :: Num a => Int -> a -> Int 

(se ve que esto no hace que el reto de cualquier fácil o más difícil, ¿verdad?)

Ahora, observe que tal implementación necesitará poder operar en todos los tipos a que se encuentren en la clase de tipo Num. (Esto es lo que una variable de tipo en un tipo denota: cuantificación universal). Nota: esto no es lo mismo (en realidad, es lo opuesto) al decir que su implementación puede elegir qué a para operar); sin embargo, eso es lo que parece sugerir en su pregunta: que su implementación debe poder elegir Int como una opción para a.

Ahora, como se desea implementar este particular (.*.) en términos del (*) para valores de tipo Int, necesitamos algo de la forma

n .*. s = n * f s 

con

f :: Num a => a -> Int 

No puedo pensar una función que convierte desde un Num arbitario-tipo a a Int de una manera significativa. Por lo tanto, diría que no hay una forma significativa de hacer Int (y, por lo tanto, Wrap) una instancia de MixedRing; es decir, no es tal que la instancia se comporte como probablemente esperaría que hiciera.

+0

Muy buena explicación. En realidad hay 2 errores: Primero, uno no puede instanciar a (de la definición de clase) en la definición de instancia yb) el camino. *. se implementa causa b = a de todos modos y contradice el tipo de. *. dado en la clase. – Ingo

+0

Gracias por esta excelente explicación. Junto con las otras respuestas, así como el enlace al paquete 'VectorSpace', esto aclara el problema. – bbtrb

2

Su implementación no es lo suficientemente polimórfica.

La regla es que si escribe a en la definición de clase, no puede usar un tipo concreto en la instancia. Porque la instancia debe cumplir con la clase y la clase prometió aceptar a que es Num.

En otras palabras: exactamente la variable de clase es la que debe ser instanciada con un tipo concreto en una definición de instancia.

Ha intentado:

data Wrap a = W { get :: a } 

Tenga en cuenta que una vez Wrap a es un ejemplo, todavía se puede utilizar con las funciones que aceptan solamente Wrap Int.

+0

Sí, he intentado esto - con y sin la restricción 'instancia (Num a) => MixedRing (Ajustar a) donde' - y no sirvió de nada. – bbtrb

+0

Pero esto se debe a su implementación de. *., Consulte el comentario de 'dblhelix'. – Ingo

6

¿Qué tal algo como:

class (Num a) => MixedRing a b where 
    (.+.) :: b -> b -> b 
    (.*.) :: b -> a -> b 
    (./.) :: b -> a -> b 

que necesitará la extensión MultiParamTypeClasses.

Por cierto, me parece que la estructura matemática que está tratando de modelar es realmente módulo, no es un anillo. Con las variables de tipo indicadas anteriormente, se dice que b es un a -module.

+0

No sabía que hay clases de tipos de parámetros múltiples, esto funciona. Y tiene razón, por supuesto, el módulo es el término correcto. Físico, no matemático aquí, una aproximación es lo suficientemente buena;) – bbtrb

Cuestiones relacionadas