2012-03-26 17 views
6

Voglio fare una superclasse di Num, chiamato lineareHaskell: fare una superclasse di Num

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

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

ottengo l'errore:

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' 

Da quello che ho capito, qualcosa sulla linea di instance (Num a) => Linear a where non è corretto. (Compilare se uso le bandiere: -XFlexibleInstances -XUndecidableInstances)

C'è un modo per ottenere questo risultato senza utilizzare quelle bandiere spaventose? (e cosa al mondo è indecidibile per il codice sopra ??)

UPDATE: Aggiunto il tipo polinomiale a Lineare.

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) 

Dopo aver aggiunto polinomiale, essa non viene compilato anche con quelle bandiere e dare l'errore:

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

puoi per favore indicare perché sono necessari; e l'indecidibilità fa paura :) – Karan

+0

In Haskell, molte cose hanno nomi spaventosi di cui nessuno si preoccuperebbe per un secondo in altre lingue. – leftaroundabout

risposta

10

Il rapporto lingua non permette istanze del form instance Class a where..., quindi l'unico modo per evitare FlexibleInstances (che non fa paura in meno) sarebbe quella di utilizzare un wrapper 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.

L'estensione UndecidableInstances è necessaria perché il vincolo Num a non è inferiore alla testa dell'istanza (utilizza le stesse variabili di tipo lo stesso numero di volte), pertanto il compilatore non può dimostrare in anticipo che la verifica del tipo verrà interrotta. Quindi devi promettere al compilatore che il controllo di tipo terminerà perché accetti il ​​programma (in realtà non farà il loop con GHC, che ha uno stack di contesto che controlla la profondità di ricorsione del type checker, quindi se il controllo di tipo non lo fa t terminare abbastanza presto, fallirà la compilazione con "stack di contesto superato" - è possibile impostare la dimensione con -fcontext-stack=N).

Questa estensione sembra molto più spaventosa di quanto non sia. Fondamentalmente tutto ciò che dice è il compilatore "Fidati di me, il controllo dei tipi terminerà", quindi il compilatore inizierà senza sapere per certo che finirà.

Ma, cosa stai cercando di ottenere? Quello che attualmente si dispone,

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

dice "ogni tipo è un esempio di lineare, e se si tenta di utilizzare aggiungere a un tipo non un'istanza di Num, che è un errore di compilazione". Non è molto utile. Non è possibile aggiungere ulteriori istanze per tipi non appartenenti a Num, a meno che non si abiliti anche OverlappingInstances e possibilmente IncoherentInstances. E quelle estensioni sono spaventose, dovrebbero essere usate appena e solo quando sai cosa stai facendo.

+0

grazie per aver spiegato questo, ma non c'è un modo standard/predefinito di creare una superclasse che non richiede all'utente di fornire al compilatore alcuna assicurazione? – Karan

+0

Voglio fare alcune istanze di Linear, e voglio che tutto ciò che è Num sia anche Linear. (cioè Linear è la superclasse di Num e ha più istanze di Num) – Karan

+0

Sono arrivato a sospettare di questo. Non è quello che di solito si chiama una superclasse, però. Tuttavia, questo ti porterà nel territorio di 'OverlappingInstances 'e non è molto accogliente. Per 'Polinomiale', avrebbe senso avere un 'istanza Num a => Num (Polinomiale a) dove ...'. 'abs' e' signum' sarebbero un po 'sospettosi, ma tutto il resto in 'Num' ha senso immediato per i polinomi. –

3

C'è uno proposal per consentire la dichiarazione di superclassi. AFAIK non è ancora implementato, ma siccome GHC è open source, puoi cambiarlo se vuoi;)

+0

puoi suggerire una soluzione temporanea nel frattempo (che non implica la creazione manuale di qualsiasi tipo di Num (cioè Int, Float ecc.) Di una istanza di Linear) – Karan

+0

La domanda è, cosa vuoi veramente fare. A seconda del problema, potrebbero esserci diverse soluzioni al tuo problema. – fuz

Problemi correlati