2013-05-03 15 views
6

Jestem nowy dla Haskella i trochę nie rozumiem, jak działają klasy typów. Oto uproszczony przykład coś próbuję zrobić:Jak powinny być stosowane typy w klasach typu Haskell?

data ListOfInts = ListOfInts {value :: [Int]} 
data ListOfDoubles = ListOfDoubles {value :: [Double]} 

class Incrementable a where 
    increment :: a -> a 

instance Incrementable ListOfInts where 
    increment ints = map (\x -> x + 1) ints 

instance Incrementable ListOfDoubles where 
    increment doubles = map (\x -> x + 1) doubles 

(. Zdaję sobie sprawę, że zwiększając każdy element listy można zrobić bardzo prosto, ale jest to po prostu uproszczona wersja bardziej złożonego problemu)

Kompilator mówi, że mam wiele deklaracji value. Jeśli zmienić definicje ListOfInts i ListOfDoubles następująco:.

type ListOfInts = [Int] 
type ListOfDoubles = [Double] 

Następnie kompilator mówi „nielegalne deklarację Instancji" Incrementable ListOfInts”(i podobnie dla ListOfDoubles jeśli używam newtype, np newtype ListOfInts = ListOfInts [Int], wówczas kompilator mówi mi: "Nie można dopasować oczekiwanego typu" ListOfInts "do rzeczywistego typu" [b0] "" (i podobnie do ListOfDoubles.

Moje zrozumienie klas typów polega na tym, że ułatwiają polimorfizm, ale wyraźnie brakuje mi czegoś W pierwszym przykładzie powyżej kompilator widzi, że parametr type a odnosi się do reco rd z polem o nazwie value i wygląda na to, że próbuję zdefiniować increment dla tego typu na wiele sposobów (zamiast widzieć dwa różne typy, taki, który ma pole, którego typ listy Int s, i drugi, którego typ to jest lista Double s)? I podobnie do innych prób?

Z góry dziękuję.

+1

mapa oczekuje na listę, dajesz jej ListOfInts – Arjan

Odpowiedz

17

Naprawdę widzisz dwa oddzielne problemy, więc adresuję je jako takie.

Pierwszy z nich to pole value. Rekordy Haskella działają w nieco dziwny sposób: po określeniu pola jest ono automatycznie dodawane do bieżącego zakresu jako funkcja. Zasadniczo, można myśleć

data ListOfInts = ListOfInts {value :: [Int]} 

jak cukier składni dla:

data ListOfInts = ListOfInts [Int] 

value :: ListOfInt -> [Int] 
value (ListOfInts v) = v 

Więc o dwa zapisy o tej samej nazwie pola jest po prostu tak, jakby dwie różne funkcje o tej samej nazwie - oni zakładka. Właśnie dlatego Twój pierwszy błąd mówi, że wielokrotnie zadeklarowałeś values.

Sposób, aby rozwiązać ten problem byłoby zdefiniować typy bez używając składni rekordową, jak ja powyżej:

data ListOfInts = ListOfInts [Int] 
data ListOfDoubles = ListOfDoubles [Double] 

Gdy używany type zamiast data, po prostu stworzył typ synonim raczej niż nowy typ. Korzystanie

type ListOfInts = [Int] 

oznacza, że ​​ListOfInts jest taka sama jak tylko [Int]. Z różnych powodów nie można domyślnie używać typów w instancjach klasy. Ma to sens - byłoby bardzo łatwo popełnić błąd, próbując napisać instancję dla [Int], a także jedną dla ListOfInts, która mogłaby zostać zerwana.

Aby zapakować pojedynczy typ, taki jak [Int] lub [Double], jest taki sam, jak przy użyciu newtype. Jednakże, newtype ma tę zaletę, że w ogóle nie generuje żadnych kosztów runtime. Więc najlepszym sposobem, aby napisać tego typu będzie faktycznie z newtype:

newtype ListOfInts = ListOfInts [Int] 
newtype ListOfDoubles = ListOfDoubles [Double] 

Ważną rzeczą do zapamiętania jest to, że podczas korzystania z data lub newtype, masz również do „otwierania” typ, jeśli chcesz dostać się na jego treść. Można to zrobić za pomocą dopasowywania wzoru:

instance Incrementable ListOfInts where 
    increment (ListOfInts ls) = ListOfInts (map (\ x -> x + 1) ls) 

ten rozpakowuje ListOfInts, mapy funkcję nad jego treścią i owija go z powrotem do góry.

Dopóki odwijasz wartość w ten sposób, twoje wystąpienia powinny działać.

Na marginesie można napisać map (\ x -> x + 1) jako map (+ 1), używając czegoś, co nazywa się "sekcją operatora". Wszystko to oznacza, że ​​niejawnie tworzysz wypełnienie lambda w zależności od argumentu operatora. Większość osób uważa, że ​​wersja map (+ 1) jest łatwiejsza do odczytania, ponieważ hałas jest mniej niepotrzebny.

+0

Wielkie dzięki, Tichon, to bardzo jasne. – Chris

Powiązane problemy