2015-09-18 17 views
8

Załóżmy Mam klasy typu Stack z jednej instancji List:Wiele parametrów typu w klasie typów?

class Stack a where 
    push :: a -> Integer -> a 
    pop :: a -> a 
    last :: a -> Integer 

data List = Empty | Element Integer List 
instance Stack List where 
    push list value = Element value list 
    pop Empty = error "No elements" 
    pop (Element _ list) = list 
    last Empty = error "No elements" 
    last (Element value _) = value 

Jak Stack musi być zdefiniowany w celu List nie powinien być ograniczony do Integer wartości?

-- class Stack (?) where ... 
data List a = Empty | Element a (List a) 
-- instance Show (List a) where ... 

Odpowiedz

6

W takim przypadku można dokonać klasę wieloparametrowego:

class Stack a b where 
    push :: a -> b -> a 
    pop :: a -> a 
    last :: a -> b 

i zdefiniować go:

instance Stack (List b) b where --You don't need to use `b`, but this make it easier to understand 
    push list value = Element value list 
    pop Empty = error "No elements" 
    pop (Element _ list) = list 
    last Empty = error "No elements" 
    last (Element value _) = value 

Należy pamiętać, że nie jest to domyślne (standaryzowany) funkcja Haskell, i że będziesz musiał go włączyć. Albo przekazując -XMultiParamTypeClasses i -XFlexibleInstances do kompilatora.

Albo można napisać:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-} 

w nagłówku pliku źródłowego.


Należy zauważyć, że można zastosować liczne b „s do a dla którego definiuje się, przykładowo (i vice versa). Może to utrudnić pracę z takimi klasami. Powiedzieć na przykład napisać typ Dummy:

data Dummy = Dummy 

można określić:

instance Stack Dummy b where 
    push x = const x 
    pop = id 
    last = const $ error "Dummy object" 

Teraz to znaczy, że masz Stack instancję dla każdej możliwej b, dzięki czemu można push i pop wszystkie rodzaje rzeczy do obiektów Dummy.

+1

Przedtem faktycznie wypróbowałem tę samą składnię, ale napisałem tylko "Stos stosu (lista a)". I zapomniałem dołączyć "FlexibleInstances" do pragmy. Teraz działa, dziękuję :) – Cubinator73

+0

Jeśli każde 'a' przyznaje co najwyżej pojedyncze' b' takie, że 'Stack a b' ma instancję, zależność funkcjonalna' a-> b' może znacząco pomóc maszynie wnioskowania typu. W przeciwnym razie każde wywołanie 'pop :: a -> a' jest niejednoznaczne:' b' nie może być określone z kontekstu 'pop'. (Oczywiście można również użyć rodziny typów.) – chi

+0

@chi: IIRC można dodać ograniczenia, takie, że będzie tylko jeden taki 'b' dla' a'. Sprawdzę to później. –

8

Należy rozważyć zastosowanie zmiennej o wyższej klasie. Zatem:

class Stack s where 
    push :: s a -> a -> s a 
    pop :: s a -> s a 
    last :: s a -> a 

data List a = Empty | Element a (List a) 

Instancja pozostaje dokładnie tak, jak to napisał (choć List ma teraz rodzaj * -> * zamiast *):

instance Stack List where 
    push list value = Element value list 
    pop Empty = error "No elements" 
    pop (Element _ list) = list 
    last Empty = error "No elements" 
    last (Element value _) = value 

Takie podejście jest czysta Haskell 2010 - to nie wymaga rozszerzenia.

Zastanów się także nad zauważeniem twoich porażek; na przykład zmieniając typ pop i last, aby odpowiednio zwrócić odpowiednio Maybe (s a) i Maybe a.

+0

To również wygląda na świetne podejście do mnie. I zdecydowanie próbuję "Może" :) – Cubinator73

+0

@ Cubinator73 To jest * zwykle * droga do zrobienia. Metoda wieloparametrowego typeclass z zależnością funkcjonalną (lub pojedyncza parametr typeclass z powiązanym typem) ma sens, jeśli rozważa się wiele monomorficznych typów stosów. – dfeuer

Powiązane problemy