2008-11-20 17 views
21

Mam więc parę typek, z których będę często korzystał, i chcę za każdym razem unikać określania ich. Zasadniczo, zamiast umieszczaćSkrót Haskell Typeclass

:: (Ord a, Fractional a, Ord b, Fractional b, ... Ord z, Fractional z) => 

na początku wszystkich moich specyfikacji typu, wolałbym postawić

:: (OrdFractional a, OrdFractional b, ... OrdFractional z) 

Tak, mój początkowy pomysł, w jaki sposób to zrobić, to po prostu zadeklarować nową typeclass

module Example where 

class (Fractional a, Ord a) => OrdFractional a 

example :: (OrdFractional a, OrdFractional b) => (a,b) -> (a,b) -> (a,b) -> Bool 
example (x1,y1) (x2,y2) (x3,y3) = (x1/x2 < x2/x3) && (y1/y2 < y2/y3) 

Ale to nie działa jak automagicznie jak chciałem to by:

% ghci 
Prelude> :l Example.hs 
Ok, modules loaded: Example. 
Prelude Example> example (1::Float,3::Float) (2,2) (3,1) 

<interactive>:1:0: 
    No instance for (OrdFractional Float) 
     arising from a use of `example' at <interactive>:1:0-39 
    Possible fix: 
     add an instance declaration for (OrdFractional Float) 
    In the expression: example (1 :: Float, 3 :: Float) (2, 2) (3, 1) 
    In the definition of `it': 
     it = example (1 :: Float, 3 :: Float) (2, 2) (3, 1) 

ręcznego tworzenia instancji wydaje się przeciągnąć tak dalej, pomyślałem, że staram się automatycznie tworzyć instancje:

module Example where 

class OrdFractional a 
instance (Fractional a, Ord a) => OrdFractional a 

example :: (OrdFractional a, OrdFractional b) => (a,b) -> (a,b) -> (a,b) -> Bool 
example (x1,y1) (x2,y2) (x3,y3) = (x1/x2 < x2/x3) && (y1/y2 < y2/y3) 

Ale kompilator nie zrobił tak:

ghc -c Example.hs 

Example.hs:4:0: 
    Illegal instance declaration for `OrdFractional a' 
     (All instance types must be of the form (T a1 ... an) 
     where a1 ... an are 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 `OrdFractional a' 

Tak jest czy mogę to zrobić?

Odpowiedz

32

Dzięki rozszerzeniu ConstraintKinds wprowadzonego w GHC 7,4 ograniczenia są teraz rodzaje rodzaju Constraint, dzięki czemu można używać zwykłych synonimy typu, aby uzyskać to, czego chcą:

{-# LANGUAGE ConstraintKinds #-} 

type OrdFractional a = (Ord a, Fractional a) 
10

Gdy kompilator mówi „Use -XFlexibleInstances”, należy spróbować dodać

{-# LANGUAGE FlexibleInstances #-} 

na górę źródła (i przejść zapoznać się z dokumentacją, aby dowiedzieć się co to oczywiście!).

W tym konkretnym przypadku będzie to uczynić swoją pracę kodu:

{-# LANGUAGE FlexibleInstances, UndecidableInstances #-} 

Elastyczne przypadki są wymagane w celu umożliwienia kontekst => na głowie instancji, a wymagane są nierozstrzygalne przypadki, ponieważ kompilator podczas rozpatrywania kontekst OrdFractional a może zakończyć dodawanie do kontekstu Fractional a i Ord a - co nie pomaga bezpośrednio w ostatecznym ustaleniu a, aw odpowiednio okropnych okolicznościach może się rozejść typechecking; kompilator naprawdę tego nie lubi. (Prawdopodobnie nie chciałby go jeśli kompilator poszedł na zawsze albo zabrakło pamięci, albo.)

3

nr

Twoje rozwiązanie z nadklasy sugerując innych klas jest najbliżej tego, co chcesz, jest możliwe w Haskell. Mimo że wymaga to ręcznych instancji tej nowej klasy, czasami jest używana, na przykład w bibliotece rewriting.

Jako, że CesarB wspomniał aliasy klasowe, robisz to, co chcesz (i więcej), ale to tylko propozycja, która istnieje od lat i nigdy nie została wdrożona, prawdopodobnie dlatego, że jest z nią wiele problemów. Zamiast tego pojawiły się różne inne propozycje, ale żadna z nich nie została wdrożona. (Listę tych propozycji można znaleźć pod numerem Haskellwiki page.) Jednym z projektów pod numerem Hac5 była modyfikacja GHC tak, aby obejmowała mały podzbiór aliasów klas o nazwie synonimy kontekstowe (które dokładnie to, o co prosisz, i nic więcej), ale niestety nigdy się nie skończyło.

+3

Nowe rozszerzenie ConstraintKinds powinien umożliwić synonimy kontekstowych. –