Różnica między State, ST, IORef i MVar

Pracuję nad napisz sobie schemat w 48 godzin (jestem do około 85 godzin) i doszedłem do części o dodawanie zmiennych i przydziałów. W tym rozdziale jest duży skok koncepcyjny i żałuję, że nie zrobiono tego w dwóch krokach z dobrą refaktoryzacją pomiędzy, a nie skokiem prosto do ostatecznego rozwiązania. W każdym razie ...

Zgubiłem się z wieloma różnymi klasami, które zdają się służyć temu samemu celowi.: State, ST, IORef, i MVar. Pierwsze trzy są wymienione w tekście, podczas gdy ostatnie wydaje się być preferowaną odpowiedzią na wiele pytań StackOverflow dotyczących pierwszych trzech. Wszystkie wydają się posiadać stan pomiędzy kolejnymi inwokacjami. Czym są i czym się różnią od siebie?

W szczególności te zdania nie mają sensu:

Zamiast tego, używamy funkcji o nazwie state threads , pozwalając Haskellowi zarządzać zbiorczym stanem dla nas. To pozwala zmienne zmienne traktujemy tak, jak w każdym innym języku programowania, używając funkcji do pobierania lub ustawiania zmiennych.

I

Moduł IORef pozwala używać zmiennych stanowych w ramach IO monad.

Wszystko to sprawia, że linia type ENV = IORef [(String, IORef LispVal)] jest myląca-dlaczego druga IORef? Co się zepsuje, jeśli zamiast tego napiszę type ENV = State [(String, LispVal)]?

Author: Robin Green, 2011-04-05

3 answers

Monada stanu: model stanu zmiennego

State monad jest czysto funkcjonalnym środowiskiem dla programów ze stanem, z prostym API:

  • get
  • put

Dokumentacja w pakiecie mtl.

Monada stanu jest powszechnie używana, gdy wymaga stanu w jednym wątku sterowania. W rzeczywistości nie używa mutowalnego stanu w swojej implementacji. Zamiast tego program jest parametryzowany przez wartość state (tzn. stan jest parametr dodatkowy do wszystkich obliczeń). Stan wydaje się być zmutowany tylko w jednym wątku (i nie może być współdzielony między wątkami).

The ST monad and STRefs

Monada ST jest kuzynem monady IO.

Pozwala na dowolne mutable state , zaimplementowane jako rzeczywista mutable memory na maszynie. API jest bezpieczne w programach bez efektów ubocznych, ponieważ parametr typu rank - 2 zapobiega wartościom zależnym od mutowalnego stanu z uciekam z lokalnego zasięgu.

Pozwala więc na kontrolowaną zmienność w czystych programach.

Powszechnie Używane do mutowalnych tablic i innych struktur danych, które są zmutowane, a następnie zamrożone. Jest również bardzo wydajny, ponieważ stan zmienny jest "przyspieszany sprzętowo".

Podstawowe API:

  • Control.Monad.ST
  • runST -- Uruchom nowe obliczenia efektu pamięci.
  • i strefy : wskaźniki do (lokalnych) mutowalnych komórek.
  • St-based tablice (takie jak wektor) są również powszechne.
Pomyśl o tym, jak o mniej niebezpiecznym rodzeństwie monady IO. Lub IO, gdzie można tylko odczytywać i zapisywać do pamięci.

IOREF: strefy w IO

Są to strefy (patrz wyżej) w MONADZIE IO. Nie mają takich samych gwarancji bezpieczeństwa jak strefy o lokalizacji.

MVars: IORefs z zamkami

Jak strefy lub IORefs, ale z zamkiem dołączonym, dla bezpiecznego równoczesnego dostępu z wielu nici. IORefs i strefy są bezpieczne tylko w Ustawieniach wielowątkowych, gdy używana jest atomicModifyIORef (operacja atomowa compare-and-swap). Mvary są bardziej ogólnym mechanizmem bezpiecznego udostępniania mutowalnego stanu.

Ogólnie, w Haskell, używać MVars lub TVars (STM-based mutable cells), nad STRef lub IORef.

 106
Author: Don Stewart,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-04 23:56:16

Ok, zacznę od IORef. IORef dostarcza wartość, która jest zmienna w MONADZIE IO. To tylko odniesienie do niektórych danych i jak każde odniesienie, istnieją funkcje, które pozwalają zmienić dane, do których się odnosi. W Haskell wszystkie te funkcje działają w IO. Możesz myśleć o tym jak o bazie danych, pliku lub innym zewnętrznym magazynie danych - możesz pobrać i ustawić w nim dane, ale wymaga to przejścia przez IO. Powodem, dla którego IO jest w ogóle konieczne, jest to, że Haskell jest pure ; kompilator potrzebuje sposobu, aby wiedzieć, do których danych odnosi się odniesienie w danym momencie(Czytaj sigfpe ' s "You could have invented monads" blogpost).

MVars są w zasadzie tym samym co IORef, z wyjątkiem dwóch bardzo ważnych różnic. {[3] } jest prymitywną współbieżnością, więc jest przeznaczona do dostępu z wielu wątków. Druga różnica polega na tym, że an MVar jest pudełkiem, które może być pełne lub puste. Więc gdzie an IORef Int zawsze ma Int (lub jest dnem), an MVar Int może mieć Int lub może być pusty. Jeśli wątek spróbuje odczytać wartość z pustego MVar, blokuje się do momentu wypełnienia MVar (przez inny wątek). Zasadniczo MVar a jest odpowiednikiem IORef (Maybe a) z dodatkowymi semantykami, które są przydatne dla współbieżności.

State jest monadą, która zapewnia stan zmienny, niekoniecznie z IO. W rzeczywistości jest to szczególnie przydatne dla czystych obliczeń. Jeśli masz algorytm, który używa stanu, ale nie IO, monada State jest często eleganckie rozwiązanie.

Istnieje również wersja monad transformer State, StateT. To często jest używane do przechowywania danych konfiguracyjnych programu lub typów stanu "game-world-state" w aplikacjach.

ST to coś nieco innego. Główną strukturą danych w ST jest STRef, która jest jak IORef, ale z inną monadą. Monad ST używa trickery type system ("wątki stanu", o których wspominają dokumenty), aby upewnić się, że zmienne dane nie mogą uciec z monad; oznacza to, że gdy uruchamiasz obliczenia ST, otrzymujesz czysty wynik. Powodem, dla którego ST jest interesujący jest to, że jest to prymitywna monada, taka jak IO, pozwalająca obliczeniom na wykonywanie niskopoziomowych manipulacji na bajtach i wskaźnikach. Oznacza to, że ST może zapewnić czysty interfejs podczas korzystania z niskopoziomowych operacji na zmiennych danych, co oznacza, że jest bardzo szybki. Z perspektywy programu, to tak, jakby obliczenia ST przebiegały w osobnym wątku z thread-local storage.

 35
Author: John L,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-04 23:55:57

Inni zrobili podstawowe rzeczy, ale aby odpowiedzieć na bezpośrednie pytanie:

Wszystko to sprawia, że typ linii ENV = IORef [(String, IORef LispVal)] mylące. Dlaczego drugi IORef? Co? złamie się, jeśli zrobię type ENV = State [(String, LispVal)] zamiast tego?

Lisp jest językiem funkcjonalnym o zmiennym stanie i zasięgu leksykalnym. Wyobraź sobie, że zamknąłeś się przez zmienną. Teraz masz odniesienie do tej zmiennej wiszące wewnątrz jakiejś innej funkcji -- powiedzmy (w stylu Haskell pseudocode) (printIt, setIt) = let x = 5 in (\ () -> print x, \y -> set x y). Teraz masz dwie funkcje -- jeden drukuje x, a drugi ustawia jego wartość. Kiedy oceniasz printIt, chcesz wyszukać nazwę X w środowisku początkowym, w którym zdefiniowano printIt, ale chcesz wyszukać wartość , do której nazwa jest związana w środowisku, w którym printIt jest wywołane (po wywołaniu setIt może być wywołana dowolną ilość razy).

Są na to sposoby oprócz dwóch Iorefów, ale na pewno potrzebujesz czegoś więcej niż ten drugi typ, który zaproponowałeś, co nie pozwala Ci zmieniaj wartości, do których nazwy są związane w sposób leksykalny. Wygoogluj "funargs problem" dla wielu ciekawych prehistorii.

 17
Author: sclv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-05 14:19:49