Dlaczego niektóre języki funkcjonalne potrzebują pamięci transakcyjnej oprogramowania?

24

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?

Michael Spector
źródło
Chciałbym tylko połączyć ten interesujący artykuł, który wyjaśnia całkiem sporo.
Falcon
1
Żeby było jasne, wszystkie języki funkcjonalne zachowują stan, ale czystość wskazuje, że wartość zmiennej nie zmienia się po jej ustawieniu.
Robert Harvey

Odpowiedzi:

13

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:

  • Badania : STM jest gorącym tematem badawczym, a badacze języków programowania często wolą pracować z funkcjonalnymi językami (temat sam w sobie, a ponadto łatwiej jest stworzyć „dowody” na temat zachowania programu)
  • Blokowanie nie komponuje : STM można postrzegać jako alternatywę dla opartych na blokadach podejść do współbieżności, które zaczynają napotykać problemy podczas skalowania do złożonych systemów przez komponowanie różnych komponentów. Jest to prawdopodobnie główny „pragmatyczny” powód STM
  • STM dobrze pasuje do niezmienności : Jeśli masz dużą niezmienną strukturę, chcesz upewnić się, że pozostaje niezmienna, więc nie chcesz, aby jakiś inny wątek wchodził i mutował jakiś podelement. Podobnie, jeśli możesz zagwarantować niezmienność wspomnianej struktury danych, możesz niezawodnie traktować ją jako stabilną „wartość” w systemie STM.

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

mikera
źródło
3

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.

Vagif Verdi
źródło
2

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.

Will Ware
źródło
Prawdopodobnie masz na myśli stan zmienny. Wszystkie programy zachowują stan, nawet te funkcjonalne.
Robert Harvey,
Masz rację. Najwyraźniej nie spędzam wystarczająco dużo czasu na programowaniu funkcjonalnym, aby to przegapić.
Will Ware,