2016-08-12 6 views
7

Załóżmy, żeHaskell styl i typeclass projekt (powinny być zminimalizowane typeclasses?)

class A a where 
    m1 :: a -> a 
    m2 :: a -> a 
    m3 :: a -> a 
    ... 

gdzie to jest możliwe napisanie domyślną implementację dla M2 i M3 za pomocą M1.

Czy lepiej jest zostawić m2 i m3 w A lub zapisać je jako dodatkowe funkcje na zewnątrz A? m2 :: A a => a -> a Innymi słowy, warto zminimalizować interfejs API klasy czy ma to znaczenie?

sprawdziłem (szybko) kilka przewodników stylu, jak programmin guidelines a niektóre z linków w large-scale design -question, a niektóre książki (rzemieślnicze & RWH), ale nie mógł znaleźć zalecenie do tego. Jeśli istnieją prezentacje, blogi lub książki poświęcone tego rodzaju problemom, czy możesz podzielić się wskazówkami?

wątek listy adresów e-mail type class design omawia to i może podkreślać minimalizację.

+2

Czy twoja klasa ma jakieś prawa dotyczące zachowania 'm2' i' m3'? Rozważmy "mconcat", który ma domyślną wartość, którą można przesłonić, ale oczekuje się, że będzie równoznaczny z powtarzającą się aplikacją 'mappend'. – chepner

+0

Co do tego, czy można zastąpić wartość domyślną, jeśli jest to tylko zewnętrznie zdefiniowana funkcja? – chepner

+0

W moim przypadku tak, robię: Gram z "sparametryzowaną równością", a pytanie w pewnym sensie dotyczy również klas Eq i ord. Myślę, że jest to wygodne, jeśli możesz wybrać, jaką metodę pisać, podczas tworzenia instancji. Ale co z min & max? – Gspia

Odpowiedz

9

Należy je przechowywać w klasie jeśli

  • Istnieje wiele różnych zestawów minimalnych kompletnych definicji dla tej klasy, i nie można a priori powiedzieć, co jest bardziej proste/elegancki dla danej instancji.
  • Można zasadnie oczekiwać, że określone typy (na przykład nieposortowane wektory) pozwolą na bardziej wydajne implementacje niż domyślne implementacje. Jeśli nie można ich zastąpić metodami, musisz przepisać reguły dla tego rodzaju optymalizacji.

Przykładem standardowej klasy o dużej liczbie "zbędnych" metod jest (w nowszych wersjach) Foldable. Częściowo wynika to z przyczyn historycznych, ale także dlatego, że różne pojemniki mogą mieć bardzo różne właściwości typu stricness itd., Więc domyślne implementacje niektórych metod mogą być naprawdę złe dla wydajności niektórych instancji.

Jeśli żadna z tych okoliczności nie dotyczy klasy, należy zachować m2 i m3 poza zajęciami. (Zawsze możesz umieścić je później: jeśli podasz domyślną implementację, nie złamie to nikogo kodu.)

+0

"zbędne", być może :) – chepner

+0

Inne dobre przykłady: '<$' w 'Functor' i' <*' and '*> 'w' Applicative'. – dfeuer

+1

Interesujące, nie wiedziałem, że '<$' było w 'Funktorze'. (Rzeczywiście Prelude nie eksportuje go, więc metoda nie pojawia się w Haddockach.) – leftaroundabout