2012-03-26 15 views
6

chcę zrobić nadklasą Num, zwany normalnyHaskell: making nadklasą Num

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

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

pojawia się błąd:

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' 

Z tego co rozumiem, coś o linii instance (Num a) => Linear a where jest nieprawidłowe. (Kompiluje się, jeśli używam flag: -XFlexibleInstances -XUndecidableInstances)

Czy istnieje sposób na osiągnięcie tego bez użycia tych przerażających flag? (i co na świecie jest nie do pojęcia o powyższym kodzie?)

UPDATE: Dodano typ wielomianowy do liniowego.

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) 

Po dodaniu wielomianu, to nie skompilować ze nawet tych flag i dać błąd:

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

czy możesz wskazać, dlaczego są potrzebne; a nierozstrzygalność jest przerażająca :) – Karan

+0

W Haskell wiele rzeczy ma przerażające nazwy, na które nikt nie będzie musiał się martwić przez sekundę w innych językach. – leftaroundabout

Odpowiedz

10

Raport język nie pozwala wystąpień postaci instance Class a where..., więc jedynym sposobem unikać FlexibleInstances (co nie jest przerażające w najmniejszym) byłoby wykorzystanie owijki 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 (+) 

fuj.

Rozszerzenie jest potrzebne, ponieważ ograniczenie Num a nie jest mniejsze niż ograniczenie do instancji (używa zmiennych tego samego typu tyle samo razy), aby kompilator nie mógł z wyprzedzeniem sprawdzić, czy sprawdzanie typu zakończy się. Tak więc musisz obiecać kompilatorowi, że sprawdzanie typu zakończy się, aby zaakceptować program (nie będzie on faktycznie pętlą z GHC, który ma stos kontekstowy, który kontroluje głębokość rekursji kontrolera, więc jeśli sprawdzanie typu nie ' t wkrótce zakończy się niepowodzeniem kompilacji z "przekroczonym stosem kontekstów" - możesz ustawić rozmiar przy pomocy -fcontext-stack=N).

To rozszerzenie brzmi znacznie bardziej przerażająco, niż jest. Zasadniczo wszystko, co robi, to powiedzieć kompilatorowi "Zaufaj mi, sprawdzanie typu zakończy się", więc kompilator uruchomi się, nie wiedząc na pewno, że to się skończy.

Ale, co próbujesz osiągnąć? Co masz aktualnie,

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

mówi „każdy typ jest instancją liniowy, a jeśli spróbujesz użyć dodać co typu nie instancją Num, że jest błąd czasu kompilacji”. To nie jest zbyt użyteczne. Nie możesz dodać kolejnych instancji dla typów nienależących do Num, chyba że włączysz także OverlappingInstances i prawdopodobnie IncoherentInstances. A te rozszerzenia są przerażające, powinny być używane ledwie i tylko wtedy, gdy wiesz, co robisz.

+0

dziękuję za wyjaśnienie tego, ale czy nie istnieje standardowy/domyślny sposób tworzenia superklasy, która nie wymaga od użytkownika dostarczenia kompilatorowi żadnych zapewnień? – Karan

+0

Chcę zrobić kilka wystąpień Linear, i chcę, aby wszystko to jest Num, również być Linearne. (Liniowy jest nadklasą Num i ma więcej instancji niż Num) – Karan

+0

Przyszedłem podejrzewać takie. Jednak to nie jest tak zwana superklasa. To jednak przeniesie Cię na terytorium "OverlappingInstances", a to nie jest zbyt przytulne. Dla 'Wielomianowy', miałoby sens mieć' instancję Num a => Num (Wielomian a) gdzie ... '. 'abs' i' signum' byłyby trochę podejrzane, ale wszystko inne w 'Num' ma natychmiastowy sens dla wielomianów. –

3

Istnieje proposal, aby umożliwić deklarację nadklas. AFAIK nie jest jeszcze zaimplementowany, ale ponieważ GHC jest open source, możesz to zmienić, jeśli chcesz;)

+0

możesz zaproponować obejście tego problemu w międzyczasie (co nie oznacza, że ​​wszystkie elementy typu Num (np. Int, Float itp.) Są instancją Linear ręcznie). – Karan

+0

Pytanie brzmi, co tak naprawdę chcesz zrobić. W zależności od twojego problemu mogą istnieć różne rozwiązania twojego problemu. – fuz