State, ST, IORef ve MVar arasındaki fark


92

48 Saat İçinde Kendinize Bir Şema Yazın üzerinde çalışıyorum (yaklaşık 85 saate kadar varım) ve Değişkenler ve Atamalar Ekleme kısmına geldim . Bu bölümde büyük bir kavramsal sıçrama var ve keşke doğrudan nihai çözüme atlamak yerine iki adımda iyi bir yeniden düzenleme ile yapılsaydı. Her neyse ...

Ben aynı amaca hizmet görünmektedir farklı sınıfların bir dizi kayıp kazanılmış ettik: State, ST, IORef, ve MVar. İlk üçü metinde belirtilirken, sonuncusu ilk üç hakkındaki birçok StackOverflow sorusunun tercih edilen cevabı gibi görünüyor. Hepsi ardışık çağrılar arasında bir durum taşıyor gibi görünüyor.

Bunların her biri nedir ve birbirlerinden nasıl farklıdırlar?


Özellikle bu cümleler bir anlam ifade etmiyor:

Bunun yerine, Haskell'in bizim için toplam durumu yönetmesine izin veren durum dizileri adı verilen bir özellik kullanıyoruz . Bu, değişkenleri almak veya ayarlamak için işlevleri kullanarak, başka herhangi bir programlama dilinde olduğu gibi değiştirilebilir değişkenleri ele almamızı sağlar.

ve

IORef modülü , IO monad içinde durum bilgisi olan değişkenleri kullanmanıza izin verir .

Bütün bunlar çizgiyi type ENV = IORef [(String, IORef LispVal)]kafa karıştırıcı hale getiriyor - neden ikincisi IORef? Onun type ENV = State [(String, LispVal)]yerine yazarsam ne bozulur?

Yanıtlar:


120

State Monad: Değişebilir durum modeli

State monad, basit bir API ile durum içeren programlar için tamamen işlevsel bir ortamdır:

  • almak
  • koymak

Mtl paketindeki belgeler .

State monad, tek bir kontrol dizisinde duruma ihtiyaç duyulduğunda yaygın olarak kullanılır. Gerçekleştirilmesinde aslında değişken durumu kullanmaz. Bunun yerine, program durum değerine göre parametrelendirilir (yani durum, tüm hesaplamalara ek bir parametredir). Durum yalnızca tek bir iş parçacığında değiştirilmiş gibi görünüyor (ve iş parçacıkları arasında paylaşılamaz).

ST monad ve STRefler

ST monad, IO monadının sınırlı kuzenidir.

Makinede gerçek değiştirilebilir bellek olarak uygulanan rastgele değiştirilebilir duruma izin verir . API, yan etkisiz programlarda güvenli hale getirilir, çünkü rank-2 türü parametresi, değiştirilebilir duruma bağlı değerlerin yerel kapsamdan kaçmasını önler.

Bu nedenle, aksi takdirde saf programlarda kontrollü değişkenliğe izin verir.

Yaygın olarak değiştirilebilir diziler ve mutasyona uğramış, sonra dondurulmuş diğer veri yapıları için kullanılır. Aynı zamanda, değişken durum "donanım hızlandırmalı" olduğundan çok verimlidir.

Birincil API:

  • Control.Monad.ST
  • runST - yeni bir bellek etkisi hesaplaması başlatın.
  • Ve STRefs : (yerel) değişken hücrelere işaret eder.
  • ST tabanlı diziler (vektör gibi) da yaygındır.

Bunu IO monadının daha az tehlikeli kardeşi olarak düşünün. Veya yalnızca okuyup belleğe yazabileceğiniz IO.

IORef: IO'daki STRefler

Bunlar IO monadındaki STReflerdir (yukarıya bakın). Bölgeyle ilgili STRef'lerle aynı güvenlik garantilerine sahip değiller.

MVars: Kilitli IORefler

STRefs veya IORefs gibi, ancak birden fazla iş parçacığından güvenli eşzamanlı erişim için bir kilit takılı . IORef'ler ve STRef'ler yalnızca çok iş parçacıklı bir ayarda kullanılırken güvenlidir atomicModifyIORef(karşılaştırma ve değiştirme atomik işlem). MVars, değiştirilebilir durumu güvenli bir şekilde paylaşmak için daha genel bir mekanizmadır.

Genel olarak Haskell'de, STRef veya IORef üzerinden MVar veya TVar (STM tabanlı değişken hücreler) kullanın.


3
MVars'ta M ve TVar'larda T ne anlama geliyor? Tahminimce "Değişken", "İşlemsel". ST'nin Durum İpliği anlamına gelmesi ilginç.
CMCDragonkai

10
Neden MVarbunun tercih edilmesi gerektiğini söylüyorsunuz STRef? STRefsadece bir iş parçacığının onu değiştirebileceğini (ve diğer IO türlerinin meydana gelmeyeceğini) garanti eder - değişken duruma eşzamanlı erişime ihtiyacım yoksa kesinlikle daha iyi olur mu?
Benjamin Hodgson

@CMCDragonkai Her zaman M'nin muteks anlamına geldiğini varsaydım, ancak onu hiçbir yerde belgelenmiş bulamıyorum.
Andrew Thaddeus Martin

37

Tamam, ile başlayacağım IORef. IORefIO monadında değiştirilebilir bir değer sağlar. Bu sadece bazı verilere bir referanstır ve herhangi bir referans gibi, atıfta bulunduğu verileri değiştirmenize izin veren işlevler vardır. Haskell'de, tüm bu işlevler içinde çalışır IO. Bunu bir veritabanı, dosya veya başka bir harici veri deposu gibi düşünebilirsiniz - içindeki verileri alabilir ve ayarlayabilirsiniz, ancak bunu yapmak için IO'dan geçmeniz gerekir. IO'nun gerekli olmasının nedeni Haskell'in saf olmasıdır ; derleyicinin, referansın herhangi bir zamanda hangi verilere işaret ettiğini bilmek için bir yönteme ihtiyacı vardır ( sigfpe'nin "Monadları icat etmiş olabilirsiniz" blog gönderisini okuyun ).

MVariki önemli fark dışında temelde IORef ile aynı şeydir. MVareşzamanlılık ilkeldir, bu nedenle birden çok iş parçacığından erişim için tasarlanmıştır. İkinci fark, MVara'nın dolu veya boş olabilen bir kutu olmasıdır. Öyleyse, bir IORef Inther zaman bir Int(veya alt) olduğunda, bir MVar Intolabilir Intveya boş olabilir. Bir iş parçacığı boş bir değerden bir değer okumaya çalışırsa MVar, MVardoldurulana kadar (başka bir iş parçacığı tarafından) engellenecektir . Temel olarak bir MVar a, IORef (Maybe a)eşzamanlılık için yararlı olan ekstra anlambilimle eşdeğerdir .

StateIO ile zorunlu olarak değil, değişebilir durum sağlayan bir monaddır. Aslında, saf hesaplamalar için özellikle yararlıdır. Durum kullanan ancak kullanmayan bir algoritmanız varsa IO, Statemonad genellikle zarif bir çözümdür.

Devletin bir monad trafo versiyonu da vardır StateT. Bu genellikle program yapılandırma verilerini veya uygulamalarda "oyun dünyasının durumu" türlerini tutmak için kullanılır.

STbiraz farklı bir şey. Ana veri yapısı STolan STRefbir gibi olan, IORefancak farklı bir monadın ile. STMonad kullanır tür sistemi hile değiştirilebilir veri monad kaçamaz sağlamak için ( "devlet parçacığı" docs söz); yani, bir ST hesaplaması çalıştırdığınızda saf bir sonuç alırsınız. ST'nin ilginç olmasının nedeni, IO gibi ilkel bir monad olması ve hesaplamaların bytearray'ler ve işaretçiler üzerinde düşük seviyeli manipülasyonlar gerçekleştirmesine izin vermesidir. Bu ST, değişken veriler üzerinde düşük seviyeli işlemleri kullanırken saf bir arayüz sağlayabileceği anlamına gelir , yani çok hızlıdır. Programın perspektifinden, SThesaplama iş parçacığı yerel depolamalı ayrı bir iş parçacığında çalışıyor gibidir.


17

Diğerleri temel şeyleri yaptı, ancak doğrudan soruyu cevaplamak için:

Bütün bunlar çizgi tipini ENV = IORef [(String, IORef LispVal)] kafa karıştırıcı hale getiriyor . Neden ikinci IORef? Onun type ENV = State [(String, LispVal)]yerine yaparsam ne bozulur?

Lisp, değişken durumu ve sözcük kapsamı olan işlevsel bir dildir. Değişken bir değişkeni kapattığınızı hayal edin. Şimdi, başka bir işlevin içinde dolaşan bu değişkene bir referansınız var - mesela (haskell tarzı sözde kodda) (printIt, setIt) = let x = 5 in (\ () -> print x, \y -> set x y). Artık iki işleviniz var - biri x'i yazdırır ve diğeri değerini belirler. Eğer değerlendirildiğinde printIt, arama istediğiniz adı bileşeni, başlangıç ortamında x printIttanımlanmıştır, ancak arama istediğiniz değer adı olan ortamda bağlı olduğu printItbir adlandırılan sonra ( setItherhangi bir kaç kez isteyebilirken ).

Bunu yapmanın iki IORef'den başka yolları da var, ancak kesinlikle önerdiğiniz ikinci türden daha fazlasına ihtiyacınız var, bu da adların bağlı olduğu değerleri sözcüksel kapsamlı bir şekilde değiştirmenize izin vermiyor. Pek çok ilginç tarih öncesi için "funargs problemi" ni Google'da arayın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.