Jak projektujesz programy w języku Haskell lub innych funkcjonalnych językach programowania?

52

Mam pewne doświadczenie w obiektowych językach programowania, takich jak c # lub ruby. Wiem, jak zaprojektować program w stylu obiektowym, jak tworzyć klasy i obiekty oraz jak definiować relacje między nimi. Znam też pewne wzorce projektowe.

Jak ludzie piszą programy funkcjonalne? Jak oni zaczynają? Czy istnieją wzorce projektowe dla języków funkcjonalnych? Czy metodologie takie jak ekstremalne programowanie lub zwinne programowanie mają zastosowanie do języków funkcjonalnych?

Łukasz
źródło
1
powiązane pytanie: stackoverflow.com/questions/3077866/…
HaskellElephant

Odpowiedzi:

24

Piszę swoją odpowiedź głównie z myślą o Haskell, chociaż wiele pojęć stosuje się równie dobrze do innych języków funkcjonalnych, takich jak Erlang, Lisp (s) i ML. Niektóre dotyczą nawet (w pewnym stopniu) Ruby, Python, Perl i JavaScript.

Jak ludzie piszą programy funkcjonalne? Jak oni zaczynają?

Pisząc funkcje. Podczas programowania funkcjonalnego albo piszesz main, albo piszesz funkcję pomocnika. Czasami twoim głównym celem może być napisanie typu danych z różnymi istotnymi funkcjami, które na nim działają.

Programowanie funkcjonalne jest bardzo dobrze dostosowane zarówno do podejść odgórnych, jak i oddolnych. Haskell zdecydowanie zachęca do pisania programów w języku wysokiego poziomu, a następnie do definiowania szczegółów projektu wysokiego poziomu. Zobacz minimumna przykład:

minimum    :: (Ord a) => [a] -> a
minimum xs =  foldl1 min xs

Funkcja znajdowania najmniejszego elementu na liście jest zapisywana po prostu jako przejście przez listę, przy użyciu funkcji min, aby porównać każdy element z „akumulatorem” lub bieżącą wartością minimalną.

Czy istnieją wzorce projektowe dla języków funkcjonalnych?

Istnieją dwie rzeczy, które można zrównać z „wzorami projektowymi”, imho, funkcjami wyższego rzędu i monadami . Porozmawiajmy o tym pierwszym. Funkcje wyższego rzędu to funkcje, które albo przyjmują inne funkcje jako dane wejściowe, albo wytwarzają funkcje jako dane wyjściowe. Każdy język funkcjonalny ogólnie sprawia ciężkie użycia map, filterorazfold(fold jest często nazywany również „zmniejszeniem”): trzy bardzo podstawowe funkcje wyższego rzędu, które stosują funkcję do listy na różne sposoby. W piękny sposób zastępują płytkę kotła do pętli. Przekazywanie funkcji jako parametrów jest niezwykle potężnym dobrodziejstwem dla programowania; wiele „wzorców projektowych” można osiągnąć prościej, korzystając z funkcji wyższego rzędu, będąc w stanie tworzyć własne i korzystając z potężnej standardowej biblioteki, która jest pełna przydatnych funkcji.

Monady to „straszniejszy” temat. Ale nie są tak straszne. Moim ulubionym sposobem myślenia o monadach jest myślenie o nich jako o otaczaniu funkcji w bańce i nadawaniu tej funkcji supermocy (które działają tylko wewnątrz bańki). Mógłbym rozwinąć, ale świat tak naprawdę nie potrzebuje kolejnej analogii monady. Przejdę więc do szybkich przykładów. Załóżmy, że chcę użyć niedeterministycznego „wzorca projektowego”. Chcę uruchomić to samo obliczenie dla różnych różnych danych wejściowych w tym samym czasie. Nie chcę wybierać tylko jednego wejścia, chcę wybrać je wszystkie. To byłaby lista monada:

allPlus2 :: [Int] -> [Int]
allPlus2 xs = do x <- xs
                 return (x + 2)

Idiomatyczny sposób na wykonanie tego jest w rzeczywistości map, ale dla ilustracji możesz zobaczyć, jak monada listy pozwoliła mi napisać funkcję, która wygląda, jakby działała na jednej wartości, ale nadała jej moc do pracy nad każdym elementem w lista? Inne supermoce obejmują awarię, stan, interakcje ze „światem zewnętrznym” i równoległe wykonywanie. Te supermoce są bardzo silne, a większość języków programowania pozwala na szaleństwo funkcji z supermocarstwami. Większość ludzi twierdzi, że Haskell w ogóle nie pozwala na te supermoce, ale tak naprawdę Haskell po prostu zawiera je w monadach, więc ich działanie można ograniczyć i zaobserwować.

tl; dr Grokking funkcje wyższego rzędu i monady są odpowiednikami Haskella wzorców projektowych grokkinga. Kiedy poznasz te koncepcje Haskell, zaczniesz myśleć, że „wzorce projektowe” to głównie tanie obejścia do symulacji mocy Haskell.

Czy metodologie takie jak ekstremalne programowanie lub zwinne programowanie mają zastosowanie do języków funkcjonalnych?

Nie widzę nic, co wiązałoby te strategie zarządzania z żadnym paradygmatem programowania. Jak stwierdzono w phynfo, programowanie funkcjonalne praktycznie zmusza cię do rozłożenia funkcji, rozbijając duży problem na podproblemy, więc mini-kamienie milowe powinny być bułką z masłem. Istnieją narzędzia takie jak QuickCheck i Zeno do testowania, a nawet potwierdzania właściwości pisanych funkcji.

Dan Burton
źródło
„Mogę opracować, ale świat tak naprawdę nie potrzebuje kolejnej analogii monady”. - Zawsze potrzebujemy więcej analogii dla monad, a twoja jest jedną z najlepszych, jakie widziałem.
Adam Gent,
1
Świetna odpowiedź, ale myślę, że twoja dyskusja na temat wzorców projektowych jest trochę myląca. Chociaż w Haskell nie potrzebujesz wzorców projektowych w stylu OO / GOF - w rzeczywistości byłoby śmiesznie je wypróbować - same wzory są po prostu sposobem, w jaki społeczność komunikuje rozwiązania problemów, które pojawiają się wielokrotnie. Społeczność Haskell jest wciąż bardzo młoda, więc nie ma jeszcze wielu wzorców do omówienia, ale jeśli poprosisz mnie o przykłady wzorców Haskell, wspomnę o takich rzeczach jak GADT lub Arrowized FRP.
rtperson