Trudno jest dokładnie zdefiniować, czym jest „język funkcjonalny” - spośród wymienionych języków tylko Haskell jest czysto funkcjonalny (wszystkie inne mają podejście hybrydowe). Istnieją jednak pewne funkcje językowe, które są bardzo pomocne w programowaniu funkcjonalnym, a Ruby i Python nie mają ich wystarczająco dużo, aby były bardzo dobrym środowiskiem dla FP. Oto moja osobista lista kontrolna, w kolejności według ważności:
- Funkcje i zamknięcia najwyższej klasy (Ruby, Python i wszystkie inne wymienione na liście mają to).
- Gwarantowana optymalizacja wywołania ogona (Erlang, Haskell, Scala i Scheme mają to, ale nie Python, Ruby ani Clojure (jeszcze)).
- Obsługa niezmienności w języku i bibliotekach standardowych (jest to duży język, który mają wszystkie wymienione „języki funkcjonalne” (oprócz schematu), ale Ruby i Python nie).
- Obsługa na poziomie językowym referencyjnie przejrzystych (lub czystych) funkcji (o ile mi wiadomo, tylko Haskell ma to obecnie).
Konieczność (1) powinna być oczywista - funkcje wyższego rzędu są niezwykle trudne bez funkcji pierwszej klasy. Kiedy ludzie mówią, że Ruby i Python są dobrymi językami dla FP, zwykle mówią o tym. Jednak ta szczególna cecha jest konieczna, ale niewystarczająca, aby język był dobry dla programu ramowego.
(2) jest tradycyjną koniecznością dla FP, odkąd wynaleziono Schemat. Bez TCO niemożliwe jest programowanie z głęboką rekurencją, która jest jednym z kamieni węgielnych FP, ponieważ dostajesz przepełnienia stosu. Jedynym „funkcjonalnym” (według popularnej definicji) językiem, który go nie ma, jest Clojure (z powodu ograniczeń JVM), ale Clojure ma wiele różnych metod symulowania TCO. (FYI, Ruby TCO jest specyficzny dla implementacji , ale Python konkretnie go nie obsługuje ). Powodem, dla którego TCO musi być zagwarantowane, jest to, że jeśli jest specyficzne dla implementacji, głębokie funkcje rekurencyjne zepsują się z niektórymi implementacjami, więc nie można tak naprawdę w ogóle ich używaj.
(3) to kolejna wielka rzecz, którą współczesne języki funkcjonalne (zwłaszcza Haskell, Erlang, Clojure i Scala) mają w przeciwieństwie do Ruby i Pythona. Bez wchodzenia w zbyt wiele szczegółów, gwarantowana niezmienność eliminuje całe klasy błędów, szczególnie w sytuacjach współbieżnych, i pozwala na porządne rzeczy, takie jak trwałe struktury danych . Bardzo trudno jest skorzystać z tych korzyści bez wsparcia na poziomie językowym.
(4) jest dla mnie najbardziej interesującą rzeczą w językach czysto funkcjonalnych (w przeciwieństwie do języków hybrydowych). Rozważ następującą niezwykle prostą funkcję Ruby:
def add(a, b)
a + b
end
Wygląda to na czystą funkcję, ale z powodu przeciążenia operatora może mutować albo parametr, albo powodować działania niepożądane, takie jak drukowanie na konsoli. Jest mało prawdopodobne, aby ktoś przeciążył +
operatora, aby wywołać efekt uboczny, ale język nie daje żadnych gwarancji. (To samo dotyczy Pythona, choć może nie w tym konkretnym przykładzie).
Z drugiej strony w języku czysto funkcjonalnym istnieją gwarancje na poziomie językowym, że funkcje są względnie przejrzyste. Ma to wiele zalet: proste funkcje można łatwo zapamiętać; można je łatwo przetestować bez polegania na jakimkolwiek stanie globalnym; a wartości w obrębie funkcji można oceniać leniwie lub równolegle bez obawy o problemy z współbieżnością. Haskell w pełni z tego korzysta, ale nie znam wystarczającej liczby języków funkcjonalnych, aby wiedzieć, czy tak.
Mimo to można stosować techniki FP w prawie każdym języku (nawet Java). Na przykład Google MapReduce jest inspirowany funkcjonalnymi pomysłami, ale o ile wiem, nie używają żadnych „funkcjonalnych” języków w swoich dużych projektach (myślę, że głównie używają C ++, Java i Python).