2012-03-06 14 views
8

Rozważmy niektóre transformatory Monad stos, powiedziećJak widelec wewnątrz transformatora monada

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 
... 
newtype J = J { runJ :: ErrorT Foo (StateT Bar IO) a } deriving (Applicative, Functor, etc) 

a niektóre funkcje J:

peekNextQuux :: J Quux 
peekNextQuux = ... 

withJ :: J a -> IO (Either Foo a) 
withJ = ... 

Potem znalazłem się wewnątrz J kontekście. Mogę napisać

f = withJ $ peekNextQuux >>= liftIO . print 

Teraz chcę zaglądać i quuxes drukowania wewnątrz osobnym wątku wewnątrz J kontekście

g = withJ . liftIO . forkIO . forever $ peekNextQuux >>= liftIO . print 

co oczywiście nie będzie działać. Sądzę, że jest jakiś sposób na rozwiązanie tak prostego problemu, po prostu nie mogę tego rozgryźć.

Odpowiedz

8

Jak można się spodziewać, że to zadziała? Oddzielny wątek musi mieć dostęp do niektórych stanów i obsługi błędów, ponieważ J zawija StateT i ErrorT. W jaki sposób wątek powinien uzyskać do tego dostęp? Kiedy stan jest aktualizowany w nowym wątku, czy powinien on również zostać zmieniony w starym wątku? Kiedy nowy wątek zgłasza wyjątek, czy stary wątek powinien zostać zatrzymany?

To nie działa, ponieważ StateT i ErrorT są transformatorami czysto monadowymi, więc opisane przeze mnie zachowania nie są możliwe do zrealizowania. trzeba wyraźnie zdać stan do nowego wątku i uruchomić nową monady stanie nie do jego pracy:

g = withJ . ... $ do 
    state <- get 
    liftIO . forkIO $ do 
    flip execStateT state . forever $ peekNextQuux >>= liftIO . print 
+0

Dzięki, to zadziałało. –

9

Nie jestem pewien, czy to, co trzeba, ale to brzmi jak ty szukasz funkcja

forkJ :: J() -> J ThreadId 

która jest podobna do forkIO, ale działa w kontekście J. Ogólnie mówiąc wszystkie punkty dflemstr są ważne. Istnieje wiele nierozwiązanych pytań dotyczących zarządzania państwem ze względu na czystość Haskella.

Jeśli jednak chcesz trochę zrestrukturyzować swoją logikę, możesz wybrać jedną opcję (jeśli szukasz tylko oddzielnego wątku z dostępem do oryginalnego stanu po wydaniu widelca) to pakcage lifted-base, który zależy od kontroli monad. Zasadniczo otrzymasz powyższą funkcję forkJ, o ile masz IO na dole stosu transformatora.

Teraz, jeśli chcesz, aby 2 wątki komunikowały się w sposób stanowy, tak, aby błędy powstałe w potomku były propagowane do głównego wątku jako część maszyny ErrorT, po prostu nie jest to możliwe (jak wyjaśnił dflemstr). Można jednak ustanowić kanał komunikacji między dwoma wątkami za pomocą konstruktu z rodziny modułów Control.Concurrent. Jeden z poniższych modułów może mieć to, czego potrzebujesz:

Control.Concurrent.Chan 
Control.Concurrent.MVar 
Control.Concurrent.STM 
+0

Dobrze, popatrzę na "podniesioną bazę". Dzięki. –