2012-03-06 6 views
8

considerazione alcuni trasformatori monade impilano, diconoCome forcella all'interno monade trasformatore

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

e alcune funzioni in J:

peekNextQuux :: J Quux 
peekNextQuux = ... 

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

Poi mi son trovata in J contesto. Posso scrivere

f = withJ $ peekNextQuux >>= liftIO . print 

Ora voglio sbirciare e quuxes stampa all'interno thread separato all'interno J contesto

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

che ovviamente non funzionerà. Immagino che ci sia un modo per risolvere un problema così semplice, ma non riesco a capirlo.

risposta

8

Come ti aspetti che funzioni? Il thread separato deve avere accesso ad alcuni stati e alcuni errori di gestione, perché J esegue il wrapping StateT e ErrorT. Come dovrebbe il thread avere accesso a questo? Quando lo stato viene aggiornato nel nuovo thread, dovrebbe essere modificato anche nel thread precedente? Quando il nuovo thread genera un'eccezione, il thread precedente deve essere interrotto?

Non può funzionare, perché StateT e ErrorT sono trasformatori monad puri, quindi i comportamenti che ho descritto non sono possibili da implementare. È necessario passare in modo esplicito lo stato per il nuovo thread ed eseguire una nuova monade stato lì per farlo funzionare:

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

Grazie, ha funzionato. –

9

Non sono sicuro se questo è quello che ti serve, ma suona come siete alla ricerca di un funzione

forkJ :: J() -> J ThreadId 

che è simile a forkIO, ma funziona invece in contesto J. In generale tutti i punti di dflemstr sono validi. Ci sono molte domande irrisolte sulla gestione dello stato a causa della purezza di Haskell.

Tuttavia, se sei disposto a ristrutturare un po 'la tua logica, un'opzione che potrebbe funzionare per te (se tutto ciò che stai cercando è un thread separato con accesso allo stato originale quando hai lanciato la fork) è la pakage di lifted-base, che dipende da monad-control. Ti darà essenzialmente la funzione forkJ in alto, purché tu abbia IO nella parte inferiore della tua pila di trasformatori.

Ora, se si desidera che i 2 thread comunichino in modo statico, in modo tale che gli errori generati nel figlio vengano propagati al thread principale come parte del meccanismo ErrorT, ciò non è possibile (come spiegato da dflemstr). Tuttavia, è possibile stabilire un canale di comunicazione tra i 2 thread utilizzando un costrutto dalla famiglia di moduli Control.Concurrent. Uno dei seguenti moduli può avere quello che ti serve:

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

Bene, darò un'occhiata a 'lifted-base'. Grazie. –