Języki funkcjonalne z definicji nie powinny utrzymywać zmiennych stanu. Dlaczego zatem Haskell, Clojure i inni udostępniają implementacje programowej pamięci transakcyjnej (STM)? Czy istnieje konflikt między dwoma podejściami?
Języki funkcjonalne z definicji nie powinny utrzymywać zmiennych stanu. Dlaczego zatem Haskell, Clojure i inni udostępniają implementacje programowej pamięci transakcyjnej (STM)? Czy istnieje konflikt między dwoma podejściami?
Odpowiedzi:
Nie ma nic złego w funkcjonalnym języku utrzymującym zmienny stan. Nawet „czyste” języki funkcjonalne, takie jak Haskell, muszą utrzymywać stan, aby wchodzić w interakcje ze światem rzeczywistym. „Nieczyste” języki funkcjonalne, takie jak Clojure, dopuszczają działania niepożądane, które mogą obejmować stan mutacji.
Najważniejsze jest to, że języki funkcjonalne zniechęcają do zmiany stanu, chyba że naprawdę go potrzebujesz . Ogólnym stylem jest programowanie przy użyciu czystych funkcji i niezmiennych danych oraz interakcja ze „nieczystym” zmiennym stanem w określonych częściach kodu, które tego wymagają. W ten sposób możesz zachować resztę kodu jako „czystą”.
Myślę, że istnieje kilka powodów, dla których STM występuje częściej w językach funkcjonalnych:
Osobiście podoba mi się podejście Clojure do zezwalania na zmienność, ale tylko w kontekście ściśle kontrolowanych „zarządzanych referencji”, które mogą uczestniczyć w transakcjach STM. Cała reszta w tym języku jest „czysto funkcjonalna”.
;; define two accounts as managed references
(def account-a (ref 100))
(def account-b (ref 100))
;; define a transactional "transfer" function
(defn transfer [ref-1 ref-2 amount]
(dosync
(if (>= @ref-1 amount)
(do
(alter ref-1 - amount)
(alter ref-2 + amount))
(throw (Error. "Insufficient balance!")))))
;; make a stranfer
(transfer account-a account-b 75)
;; inspect the accounts
@account-a
=> 25
@account-b
=> 175
Należy zauważyć, że powyższy kod jest w pełni transakcyjny i atomowy - zewnętrzny obserwator odczytujący dwa salda w ramach innej transakcji zawsze zobaczy spójny stan atomowy, tj. Dwa salda zawsze będą sumować się do 200. Przy współbieżności opartej na blokadzie jest to zaskakująco trudny problem do rozwiązania w dużym złożonym systemie z wieloma podmiotami transakcyjnymi.
Aby uzyskać dodatkowe oświecenie, Rich Hickey doskonale objaśnia STM Clojure w tym filmie
Języki funkcjonalne z definicji nie powinny utrzymywać zmiennych stanu
Twoja definicja jest nieprawidłowa. Nie można użyć języka, który nie może utrzymać stanu.
Różnica między językami funkcjonalnymi a imperatywnymi nie polega na tym, że jeden z nich ma stan, a drugi nie. W ten sposób utrzymują państwo.
Języki imperatywne rozprzestrzeniły się po całym programie.
Języki funkcjonalne wyraźnie izolują i utrzymują stan poprzez podpisy typu. I dlatego dostarczają zaawansowane mechanizmy zarządzania stanem, takie jak STM.
Czasami program wymaga stanu zmiennego (na przykład zawartości bazy danych dla aplikacji sieci web) i byłoby wspaniale móc go używać bez utraty korzyści z programowania funkcjonalnego. W językach niefunkcjonalnych stan mutable przenika wszystko. Jeśli wyrazisz to jawnie za pomocą specjalnego API , możesz ograniczyć go do małego identyfikowalnego regionu, podczas gdy wszystko inne pozostaje czysto funkcjonalne. Zalety FP obejmują łatwiejsze debugowanie, powtarzalne testowanie jednostek, bezbolesną współbieżność oraz łatwość obsługi wielu rdzeni / GPU.