2010-12-12 15 views
12

Reading „Real World Haskell” znalazłem kilka ciekawie pytanie o typy danych:Haskell użytkowania typy danych dobrych practicies

Ten dopasowywania wzorców i pozycyjnego dostępu danych sprawiają, że wyglądają jak masz bardzo szczelne połączenie pomiędzy danymi i kod, który na nim działa (spróbuj dodać coś do książki lub gorzej zmienić typ istniejącej części ).

Zazwyczaj jest to bardzo złe w imperatywu (szczególnie oo) languages ​​... nie jest to postrzegane jako problemu w Haskell? source at RWH comments

I rzeczywiście, pisząc niektóre programy Haskell Okazało się, że kiedy robię małą zmianę danych typu konstrukcji wpływa na niemal wszystkie funkcje, które korzystają z tego typu danych. Być może istnieją pewne dobre praktyki dotyczące użycia typu danych. Jak mogę zminimalizować powiązanie kodu?

Odpowiedz

13

To, co opisujesz, jest powszechnie znane jako problem z wyrażeniem - http://en.wikipedia.org/wiki/Expression_Problem.

Konieczne jest ustalenie kompromisu, ogólnie kod w standardzie haskell, a zwłaszcza typy danych algebraicznych, często wpadają w trudny do zmiany typ, ale są łatwe do dodania do funkcji. Optymalizuje to dla dobrze zaprojektowanych, kompletnych typów danych (z przodu).

Wszystko, co powiedzieliśmy, jest kilka rzeczy, które możesz zrobić, aby zmniejszyć sprzężenie.

  • określenia dobrego funkcji bibliotecznych, definiując komplet kombinatorów i funkcja wyższego rzędu, które są przydatne do interakcji z danymi typu będzie można zmniejszyć sprzężenie.Często mówi się, że gdy kiedykolwiek myślisz o dopasowywaniu wzorów, istnieje lepsze rozwiązanie z użyciem funkcji wyższego rzędu. Jeśli szukasz takich sytuacji, znajdziesz się w lepszym miejscu.

  • Ujawnij struktury danych jako bardziej abstrakcyjne typy. Oznacza to wdrożenie wszystkich odpowiednich klas typów. Pomoże to w zdefiniowaniu funkcji biblioteki, jako że dostaniesz garść za darmo z dowolnymi klasami typów, które zaimplementujesz, na przykład spójrz na operacje wykonywane przez Functor lub Monad.

  • Ukryj (o ile to możliwe) konstruktory dowolnego typu. Konstruktorzy ujawniają szczegóły implementacji i zachęcają do łączenia. Podpowiedź: łączy się to z definiowaniem dobrego interfejsu API do interakcji z typem, konsumenci tego typu rzadko, jeśli w ogóle, muszą używać konstruktorów typów.

Wspólnota Haskell wydaje się szczególnie dobry w tym, jeśli spojrzeć w wielu bibliotekach na hackage znajdziesz naprawdę dobre przykłady realizacji zajęcia typu i narażając dobre funkcji bibliotecznych.

+0

Nie sądzę, że można się dogadać bez dopasowania do wzorca. * Jawna rekursja * jest jednak rzadko potrzebna. – delnan

+0

+1 za nadanie bestii imienia. – fuz

7

Po pierwsze, chciałbym wspomnieć, że moim zdaniem, istnieją dwa rodzaje sprzęgieł:

  • Jeden, który sprawia, że ​​przestają kodu do kompilacji po zmianie jednego i zapomnij zmienić drugi

  • Jeden, który sprawia, że ​​kod buggy po zmianie jednego i zapomnieć, aby zmienić inne

Choć obie są problematyczne, były znacznie mniej ah eadache i to chyba ten, o którym mówisz.

Myślę, że główny problem, o którym wspomniałeś, wynika z nadmiernej liczby argumentów pozycyjnych. Haskell prawie zmusza cię do posiadania argumentów pozycyjnych w zwykłych funkcjach, ale możesz ich uniknąć w swoich produktach typu (rekordy).

Po prostu używaj rekordów zamiast wielu anonimowych pól wewnątrz konstruktorów danych, a następnie możesz dopasować do nich dowolne pole, według nazwy.

bad (Blah _ y) = ...

good (Blah{y = y}) = ...

uniknąć nadmiernego korzystania krotki, zwłaszcza poza 2-krotek i hojnie utworzyć rekordy/newtypes wokół rzeczy, aby uniknąć pozycyjną znaczenie.

+1

Dziękuję za odpowiedź. Nie jestem bardzo podobna do składni rekordu, ponieważ nazwa pola staje się globalna w module. Może jest jakieś lekarstwo? – aindl

+0

IMO lekarstwo ma bardzo małe moduły :-) – Peaker

+0

@masterzim Problem zanieczyszczenia przestrzeni nazw z rekordami otrzyma poprawkę w nadchodzącej wersji GHC, która pozwoli nadać kilku typom te same nazwy rekordów, a funkcje akcesora będą uzyskać za darmo będą ogólne w stosunku do typów, które ma to pole rekordu. – kqr

10

Oprócz tego, co zostało powiedziane:

Interesującym podejściem jest „scrap your boilerplate” styl definiowania funkcji ponad typów danych, co sprawia, że ​​korzystanie z funkcji generycznych (w przeciwieństwie do wyraźnego pasujące do wzorca) określenie funkcji nad konstruktory typu danych. Patrząc na dokumenty "złomuj swój zestaw" zobaczysz przykłady funkcji, które mogą poradzić sobie ze zmianami w strukturze typu danych.

Druga metoda, jak Hibberd wskazał, jest użycie fałdy, odwzorowuje, rozwija i innych kombinatorów rekursji, aby zdefiniować swoje funkcje. Podczas pisania funkcji za pomocą funkcji wyższego rzędu, często niewielkie zmiany w typie danych podstawowych mogą być rozwiązywane w deklaracjach instancji Functor, Foldable i tak dalej.