2013-06-14 14 views
24

Mam pewne trudności, aby zrozumieć, kiedy użycie i kiedy nie używać typeclass w moim kodzie. Mam na myśli tworzenie własnych, a nie używać już zdefiniowanych typeclasses, oczywiście. Przez przykład (bardzo głupi przykład), należy zrobić:Czy powinienem używać czcionek typograficznych?

data Cars = Brakes | Wheels | Engine 
data Computers = Processor | RAM | HardDrive 

class Repairable a where 
    is_reparaible :: a -> Bool 

instance Repairable Cars where 
    is_repairable (Brakes) = True 
    is_repairable (Wheels) = False 
    is_repairable (Engine) = False 

instance Repairable Computers where 
    is_repairable (Processor) = False 
    is_repairable (RAM)  = False 
    is_repairable (HardDrive) = True 

checkState :: (Reparaible a) => a -> ... 
checkState a = ... 

(Oczywiście, jest to głupie, niekompletne przykład).

Ale to dużo dla małego użytku, nie? Dlaczego nie powinienem robić czegoś prostego i tylko definiować funkcje bez definiowania nowych typów danych i typów liter (wraz z ich instancjami).

Ten przykład jest zbyt prosty, ale w rzeczywistości często widzę coś takiego (nowe typy danych + typografia + instancje), gdy przeglądam kod Haskella na github zamiast tylko definiowania funkcji.

Kiedy należy tworzyć nowe typy danych, typy znaków itp. I kiedy należy korzystać z funkcji?

Dzięki.

Odpowiedz

40

Dlaczego nie powinienem zrobić coś prostego i tylko definiujący funkcje bez definiowania nowych typów danych i typeclasses (z ich przypadkach).

Dlaczego tak naprawdę? Możesz po prostu zdefiniować:

checkState :: (a -> Bool) -> (a -> b) -> (a -> b) -> a -> b 
checkState is_repairable repairs destroy a 
    = if (is_repairable a) then repairs a else destroy a 

Klasy typu "nadużywaj" przez cały czas. To nie znaczy, że jest idiomatyczny.

Aby odpowiedzieć na bardziej ogólne pytanie, oto kilka zasad kciuk do kiedy należy użyć klasy typu, a kiedy nie należy ich użyć:

klas typ zastosowania, jeżeli:

  • Jest tylko jeden za poprawne zachowanie danego typu

  • Klasa typ jest związane równania (czyli „prawa”), które muszą spełniać wszystkie instancje

Nie używaj zajęcia typu, jeżeli:

  • Próbujesz tylko rzeczy nazw. Właśnie do tego służą moduły i przestrzenie nazw.

  • Osoba używając klasy typu nie można rozumować o tym, jak będzie zachowywać się bez patrzenia na kodzie źródłowym przypadkach

  • się okazać, że rozszerzenia trzeba włączyć wymyka się spod kontroli

+0

Tak! To bardzo wyczerpująca odpowiedź, dziękuję bardzo! Twoje "nie używaj lekcji typu, jeśli ..." będzie szczególnie pomocne w wyborze dobrego sposobu na zrobienie czegoś. – vildric

+1

Dodałbym do tego "nie używaj czcionek tylko dla jednej metody", chociaż nie jest to absolutnie sztywna reguła, bardziej jak ogólna wskazówka. – MathematicalOrchid

+2

@MathematicalOrchid Jest to część zasady "potrzeb praw", ponieważ rzadko będziesz mieć prawa dla klasy typów za pomocą tylko jednej metody (z wyjątkiem czegoś takiego jak 'SemiGroup', gdzie masz ustawę o asocjacji) –

5

Często można użyć typu danych zamiast klasy typów, np.

data Repairable a = Repairable 
    { getRepairable :: a 
    , isRepairable :: Bool 
    , canBeRepairedWith :: [Tool] -> Bool -- just to give an example of a function 
    } 

Oczywiście trzeba przekazać tę wartość bezpośrednio, ale może to być dobre, jeśli masz wiele możliwości wyboru (np myśleć Sum i Product jako możliwe Monoid s dla numerów). Poza tym, że masz mniej więcej taką samą ekspresyjność jak w przypadku klasy typu.

+2

Nie jestem downvoter, ale. . . ta odpowiedź tak naprawdę nie odpowiada na pytanie IMHO. OP pyta, kiedy należy użyć liter. Ta odpowiedź wymienia alternatywę dla typografii, ale nie mówi, kiedy użyć tej alternatywy. (Jeśli cokolwiek, to * zakłada * zrozumienie, kiedy typografia ma sens, i oferuje alternatywę, która ma sens w wielu podobnych przypadkach.) – ruakh

Powiązane problemy