2012-03-26 13 views
6

Quiero hacer una superclase de Num, llamado LinealHaskell: hacer una superclase de Num

class Linear a where 
    add :: a -> a -> a 

instance (Num a) => Linear a where 
    add = (+) 

me sale el error:

Illegal instance declaration for `Linear a' 
    (All instance types must be of the form (T a1 ... an) 
    where a1 ... an are *distinct type variables*, 
    and each type variable appears at most once in the instance head. 
    Use -XFlexibleInstances if you want to disable this.) 
In the instance declaration for `Linear a' 

Por lo que sé, algo sobre la línea instance (Num a) => Linear a where Es incorrecto. (Compila si uso las banderas: -XFlexibleInstances -XUndecidableInstances)

¿Hay alguna manera de lograr esto sin usar esas banderas de miedo? (y lo que en el mundo es indecidible sobre el código de arriba?)

ACTUALIZACIÓN: Tipo de polinomio agregado a Linear.

newtype Polynomial a = Polynomial (a,[a]) deriving Show-- list of coeffients 

instance (Linear a) => Linear (Polynomial a) 
     where 
      add (Polynomial (c1, l1)) (Polynomial (c2, l2)) 
      = Polynomial (add c1 c2, zipWith (add) l1 l2) 

p1 = Polynomial (0, [3,4,5]) 
p2 = Polynomial (0, []) 

main = putStrLn $ show ((add p1 p2):: Polynomial Int) 

Después de añadir polinomio, no compila con incluso las banderas y dar el error:

Overlapping instances for Linear (Polynomial Int) 
    arising from a use of `add' 
Matching instances: 
    instance Num a => Linear a -- Defined at Algebra.hs:22:10-28 
    instance Linear a => Linear (Polynomial a) 
    -- Defined at Algebra.hs:25:10-44 
In the first argument of `show', namely 
    `((add p1 p2) :: Polynomial Int)' 
In the second argument of `($)', namely 
    `show ((add p1 p2) :: Polynomial Int)' 
In the expression: putStrLn $ show ((add p1 p2) :: Polynomial Int) 
+0

¿podría indicar por qué se necesitan? y la indecidibilidad da miedo :) – Karan

+0

En Haskell, muchas cosas tienen nombres espeluznantes que nadie se preocuparía por un segundo en otros idiomas. – leftaroundabout

Respuesta

10

El informe de la lengua no permite que las instancias de la forma instance Class a where..., por lo que la única manera de evitar FlexibleInstances (que no es de miedo en el menos) sería utilizar un envoltorio newtype,

newtype LinearType a = Linear a 

liftLin2 :: (a -> b -> c) -> LinearType a -> LinearType b -> LinearType c 
liftLin2 op (Linear x) (Linear y) = Linear (op x y) 

instance Num a => Linear (LinearType a) where 
    add = liftLin2 (+) 

Yuck.

La extensión UndecidableInstances es necesaria porque la restricción Num a no es menor que la cabeza instancia (que utiliza las mismas variables de tipo el mismo número de veces), por lo que el compilador no puede probar de antemano que la verificación de tipos terminará. Por lo tanto, tienes que prometerle al compilador que la verificación de tipos terminará para que acepte el programa (no enlazará en realidad con GHC, que tiene una pila de contexto que controla la profundidad de recursión del verificador de tipos, por lo que si no se comprueba el tipo) Terminará lo suficientemente pronto, fallará la compilación con "stack de contexto excedido" - puede establecer el tamaño con -fcontext-stack=N).

Esta extensión suena mucho más aterradora de lo que es. Básicamente, todo lo que hace es decirle al compilador "Créeme, la verificación del tipo terminará", por lo que el compilador se iniciará sin saber con certeza si finalizará.

Pero, ¿qué estás tratando de lograr? Lo que tienes actualmente,

instance (Num a) => Linear a where 
    add = (+) 

dice "todo tipo es una instancia de lineal, y si intenta utilizar al agregar un tipo no una instancia de Num, que es un error en tiempo de compilación". No es muy útil. No puede agregar más instancias para los tipos que no pertenecen a Num, a menos que también habilite OverlappingInstances y posiblemente IncoherentInstances. Y esas extensiones son aterradoras, se deben usar apenas y solo cuando sepas lo que estás haciendo.

+0

gracias por explicar esto, pero ¿no hay una forma estándar/predeterminada de crear una superclase que no requiera que el usuario brinde garantías al compilador? – Karan

+0

Quiero hacer algunas instancias de Linear, y quiero que todo esto sea Num también sea Lineal. (es decir, lineal es la clase superior de Num y tiene más instancias que Num) – Karan

+0

. He llegado a sospechar eso. Sin embargo, eso no es lo que generalmente se llama una superclase. Sin embargo, eso te llevaría al territorio 'OverlappingInstances', y eso no es muy acogedor. Para 'Polinomio', tendría sentido tener un' instancia Num a => Num (Polinomio a) donde ... '. 'abs' y' signum' serían un poco sospechosos, pero todo lo demás en 'Num' tiene sentido inmediato para los polinomios. –

3

Hay un proposal para permitir la declaración de superclases. AFAIK aún no está implementado, pero como GHC es de código abierto, puede cambiarlo si lo desea;)

+0

puede sugerir una solución alternativa en la media hora (que no implican la realización de todo tipo de Num (es decir, int, float, etc) una instancia de Linear manualmente) – Karan

+0

La pregunta es, lo que realmente quiere hacer. Dependiendo de su problema, puede haber diferentes soluciones a su problema. – fuz

Cuestiones relacionadas