aktualizacja 2020-08-03: nie popieram już treści w tym poście. Myślę, że ogólny sentyment jest marginalnie dokładny, jednak szczegóły w poście są nieprawidłowe (jak wielu zauważyło na przestrzeni lat).

jak już wspomniano, remove-if-notparametry start/count zachowują się inaczej i nie można ich łatwo oddzielić od funkcji, kompromis projektowy, który doceniam.

jak zauważono i zaznaczono, Clojure pozwala na kompozycyjny styl i lenistwo. Chciałbym również zwrócić uwagę, że lenistwo nie jest do końca konieczne do stream fusion (wystarczy czystość), ale pomaga w ergonomii.

nie mam ochoty ciągnąć artykułu do końca. Od lat krąży na HN / reddit. Czuję się dobrze przyznając, że nie jest zbyt dobrze. Zapraszam do czytania dalej, ale weź to z przymrużeniem oka.

jedna różnica w filozofii Lispu (np. Common Lisp, Emacs Lisp) i Haskella polega na tym, że ten ostatni korzysta z wielu drobnych funkcji, które wykonują jedno zadanie. Jest to znane jako composability lub filozofia Uniksa. W Lispie procedura zazwyczaj akceptuje wiele opcji, które konfigurują jej zachowanie. Nazywa się to monolityzmem lub wykonywaniem procedur takich jak zlewozmywak kuchenny lub nóż armii szwajcarskiej.

który z nich jest lepszy można omówić w innym poście. Chcę tylko wyjaśnić, że w filozofii i praktyce rzeczywiście istnieje różnica. Po napisaniu mojego uczciwego udziału w nietrywialnym Emacsie Lispie (i małego udziału w Common Lispie; utrzymywałem Systemy Common Lispu) i mojego uczciwego udziału w nietrywialnym Haskellu, myślę, że jestem w stanie ocenić.

pełne ujawnienie: Przyjrzymy się tylko kilku banalnym przykładom, które każdy może zrozumieć, z (niesprawdzoną, ale potwierdzoną) implikacją, że te przykłady są reprezentatywne dla ogólnego sposobu pisania oprogramowania w tych językach.

przykładem, który powinien być łatwo zaznajomiony z każdym programistą z dowolnego środowiska, jest praca nad listami. Na przykład CL ma procedurę remove-if-not. Jego podpis dokumentacji wygląda następująco:

pakuje kilka pomysłów w jedną procedurę.

dla porównania, Haskell ma funkcję filter :

przy stwierdzeniu problemu „weź wszystkie elementy z listy–z wyjątkiem pierwszych trzech–które spełniają predykat p, i weź tylko pierwsze pięć z nich”, w Common Lispie wyrażasz to dość zwięźle w następujący sposób:

to samo w Haskell byłoby wyrażone w następujący sposób:

różnica, która powinna być oczywista, czy znasz Haskell czy Lisp, polega na tym, że w kodzie Lispu funkcja wykonuje kilka zachowań i akceptuje argumenty, aby je skonfigurować. W kodzie Haskella używamy trzech różnych funkcji, które wykonują jedno zadanie:

operator . komponuje funkcje razem, tak jak pipes w Uniksie. Możemy wyrazić to w Uniksie w następujący sposób:

naciśnij Ctrl – D tutaj otrzymujemy:

podobnie jak pipes w Uniksie, funkcje są na tyle sprytne, że mogą być wykonywane, gdy są złożone razem–nie przemierzamy całej listy i nie generujemy nowej listy za każdym razem, każdy element jest generowany na żądanie. W rzeczywistości, dzięki stream fusion, kod zostanie skompilowany w jedną szybką pętlę.

jeśli chcemy rzeczy, które nie spełniają orzeczenia, po prostu komponujemy ponownie z not:

w Common Lispie kompozycja jest nieco bardziej słowna, ponieważ rzadko jest używana, więc zamiast tego istnieje inna funkcja do tego:

(prawdopodobnie bardziej Lispim podejściem byłoby posiadanie argumentu :not słowa kluczowego do funkcji remove-if.)

najbardziej patologicznym przykładem takiego zlewu kuchennego w Lispie jest dobrze znane makro pętli.

Problem: uzyskaj wszystkie elementy mniejsze niż 5, a potem tylko te parzyste z tego zestawu.

za pomocą makra pętli można to dość łatwo wyrazić:

w Haskell jest to wyrażone za pomocą dwóch oddzielnych funkcji:

w Haskell to samo dotyczy bibliotek wektorowych i tekstowych oraz bibliotek bajtów, które mogą być łączone. Fuzja jest głównie zaletą czystości – możesz połączyć n pętli w jedną pętlę, jeśli wiesz, że nie powodują skutków ubocznych. Taka zaleta może być również zastosowana do innych czystych języków, takich jak Idris lub PureScript lub Elm.