Dlaczego warto nie polegać na zmianie stanu?

16

To pytanie wynika z pytania /software/25569/is-haskell-worth-learning

Zasadniczo pada kilka często powtarzanych stwierdzeń na temat tego, w jaki sposób Haskell poprawia twoje umiejętności kodowania w innych językach, a ponadto dzieje się tak, ponieważ Haskell jest bezstanowy, i to dobrze.

Dlaczego?

Widziałem, jak ktoś porównuje to do pisania tylko lewą ręką lub zamykania oczu na jeden dzień i polegania na dotyku. Z pewnością jest w tym coś więcej?

Czy dotyczy to dostępu do pamięci sprzętowej, czy może czegoś innego, co stanowi duży wzrost wydajności?

ocodo
źródło
2
Haskell jest naukowcem. Obejrzałbym kilka wykładów Richa Hickeya na temat Clojure'a - tam przedstawia on zabójcze pragmatyczne argumenty (podobne do 3 punktów Javiera, ale również zrobione w prosty sposób).
Job
2
Tak, patrz clojure.org/state
LennyProgrammers
6
To, że Haskell jest „akademicki”, nie oznacza, że ​​jest niepraktyczne lub pragmatyczne.
Tikhon Jelvis

Odpowiedzi:

17

z głowy mam co najmniej trzy duże zalety:

  1. sprawia, że ​​programy są bardziej zbliżone do wyrażeń matematycznych. W matematyce xnic się nie zmienia, po prostu nie wiesz, co to jest, dopóki nie rozwiążesz równania.

  2. W końcu, nie jest zmiana stanu (po wszystkim, to jak komputer działa na niskim poziomie); ale język ogranicza się do określonych miejsc. co daje kompilatorowi ogromne możliwości przenoszenia kodu w celu jego optymalizacji, ponieważ wie, że nie zmienia niczego, od czego zależy inny kod.

  3. Współbieżny kod nie musi być synchronizowany, aby uzyskać dostęp do niezmiennych danych, więc współbieżność jest zwiększona, zarówno w systemach pamięci współdzielonej SMP (wszystkie dzisiejsze systemy wielordzeniowe), jak i w luźno powiązanych klastrach.

Javier
źródło
3
Proponcje programowania funkcjonalnego kładą największy nacisk na 3, tj. Łatwiejsze programowanie współbieżności.
Mchl
4
@Mchl: Z mojego doświadczenia kładą największy nacisk na „Łatwiej jest zrozumieć i uzasadnić”, co w pewnym sensie odpowiada 1. Chociaż przypuszczam, że mogą się różnić między społecznościami językowymi.
sepp2k
+1, bardzo kompletna odpowiedź. @ sepp2k: Wydaje mi się, że oba są ważne. Rozumowanie programu jest tym, co robimy codziennie, i prawdą jest, że jeśli nie musisz sprawdzać, czy stan nie zmienił się w jakiejś głęboko ukrytej funkcji, o wiele łatwiej jest czytać tylko metody wysokiego poziomu i łapać, co się dzieje. Pod względem sprzętowym: ponieważ coraz bardziej przechodzimy na procesory wielordzeniowe i wieloprocesorowe (myślę, że minie trochę czasu, zanim komputery domowe otrzymają wieloprocesory), posiadanie języka, który ułatwia jednoczesne programowanie, jest zaletą.
Matthieu M.
Dobra odpowiedź, nr 2 był tym, co uważałem za odpowiedź i chociaż często czytałem o zaletach przetwarzania wieloprocesowego, rzadko jest to określane jako wyraźna, świetna robota.
ocodo
1
@Yttrill, języki funkcjonalne nie są unikalne pod względem polegania na odśmiecaniu, a odśmiecanie jest znacznie łatwiejsze, gdy masz do czynienia z niezmiennymi danymi. Wykonanie dowolnego obliczenia na architekturze opartej na instrukcjach oznacza modyfikację stanu, co nie oznacza, że ​​języki funkcjonalne są trudniejsze do zrównoleglenia. Języki funkcjonalne kołyszą się równolegle; równolegle do wyszukiwania danych Haskell, jest to funkcja, której dodanie do imperatywnego języka byłoby prawie niemożliwe.
dan_waterworth
4

Oto kolejna zaleta: zmniejszone sprzężenie. Jeśli masz kod taki jak:

 function doStuff(x) { return x + y;}

a gdzie indziej masz:

 function doOtherStuff(x) { y++; return y + x;}

wówczas dwie funkcje są zależne pośrednio . Nie ma łatwego sposobu na stwierdzenie, że dzwonienie doStuffma wpływ na połączenia doOtherStuff. Bez stanu zmiennego musisz jawnie określić połączenie.

Oczywiście nie jest to problem ze wszystkimi stanami zmiennymi - problem dotyczy wszechobecnego stanu zmiennego. Prawdziwym rozwiązaniem jest domyślna niezmienność oraz pewien sposób „oznaczania” i ograniczania stanu zmienności do dokładnie tam, gdzie jest to potrzebne.

Tikhon Jelvis
źródło
+1. Wielu doświadczonych programistów wie, że nie pisać kodu takiego jak powyższy i przejście od „stanu zmiennego jest zły w tej sytuacji” do „poważnego zmniejszenia stanu, który można modyfikować i pisania bardziej funkcjonalnie”, ale to krok rozczarowująco mało.
dan_waterworth
2

Uproszczona odpowiedź brzmi: kiedy widzisz nazwę w czysto funkcjonalnym języku, wiesz, czym jest powiązana wartość, poprzez proste sprawdzenie jej definicji. Jeśli masz zmienne zmienne, możesz tylko stwierdzić, które z kilku przypisań do niego zostało wykonane jako ostatnie, więc musisz także przeanalizować przepływ sterowania, który z kolei może być warunkowy, pozostawiając ci wiele możliwości. Aby uzyskać eksplozywną eksplozję, należy jedynie wziąć pod uwagę, że RHS przypisań same w sobie zależą od zmiennych, więc trzeba je również rekurencyjnie analizować.

Najważniejsze w powyższej analizie jest to, że nie da się tego obronić bez komentarzy wyjaśniających zamiar, niezmienniki i semantykę: mogą być trudne do interpretacji i może być trudne sprawdzenie, czy semantyka jest przestrzegana w rzeczywistym kodzie.

Ta odpowiedź jest w zasadzie rozwinięciem punktu 1 Javiera.

Myślę, że jest to również wyjaśnienie popularności nieuczciwego reżimu OO: w przypadku OO stan zmiennych jest enkapsulowany, co znacznie ułatwia analizę, lokalizując mutacje do pewnego stopnia i pozwalając na znacznie bardziej solidną ekspresję i weryfikację semantyki.

Należy zauważyć, że programowanie funkcjonalne nie jest odpowiedzią. Właściwą odpowiedzią jest system, który obsługuje zarówno programowanie indukcyjne (funkcjonalne), jak i koindukcyjne (proceduralne), dzięki czemu odpowiednie narzędzia mogą obsługiwać zarówno programowanie bezstanowe, jak i stanowe. Tyle tylko, że teoria konstruktywna (funkcjonalna) jest dobrze ugruntowana, podczas gdy teoria zarządzania państwem jest jeszcze w powijakach.

Yttrill
źródło
Mieszanie programowania funkcjonalnego i imperatywnego jest dokładnie tym, co robi Haskell - normalne funkcje są funkcjonalne, a obliczenia stanowe można wyrazić za pomocą notacji, która pozwala ostrożnie izolować i kontrolować stan zmiennych (między innymi). Dlatego językiem z najbardziej praktyczną implementacją STM jest Haskell .
Tikhon Jelvis
Notacja nie ma właściwie nic do wykonywania obliczeń stanowych per se . Notacja do jest prostszą składnią na monadycznych potokach funkcyjnych. Obliczenia stanowe to tylko jedna z rzeczy, które można wyrazić za pomocą monad, a zatem i notacji.
Jonathan Sterling
Mieszane programowanie funkcjonalne i imperatywne jest tym, co robi prawie każdy istniejący język. Haskell mógł zapewnić sposób na wyodrębnienie części stanowych, ale to nie czyni z niego właściwej mieszanki: Dobroczynność bardziej przypomina to, jak powinno być (IMHO).
Yttrill
2

Jako autor Siege , DBMS napisanego w Haskell, niektórzy mogą nazwać mój pogląd na temat stanu zmiennego konfliktem. Mam nadzieję, że pokażę inaczej.

Celem stanu zmiennego jest opisanie bieżącego stanu, w którym znajduje się system. Załóżmy, że masz bloga i jest on zapleczem bazy danych, baza danych opisuje posty, które masz na swoim blogu w momencie zapytania to. Ile postów istnieje obecnie?

Porównaj to z niezmiennym stanem, który służy do przekazywania faktów. Ile postów było tam 12 sierpnia?

Fakty są łatwe do uzasadnienia, stan mutable nie. Zmienny stan nie jest jednak jakimś złym nieczystym efektem, który powinien zostać usunięty z zasięgu naszych umysłów; często potrzebujemy, aby współistniało w zmiennym świecie, w którym żyjemy, musimy tylko używać go oszczędniej.

dan_waterworth
źródło