Consider some monad transformers stack, say
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
...
newtype J = J { runJ :: ErrorT Foo (StateT Bar IO) a } deriving (Applicative, Functor, etc)
And some functions in J
:
peekNextQuux :: J Quux
peekNextQuux = ...
withJ :: J a -> IO (Either Foo a)
withJ = ...
Then I found myself inside J
context. I can write
f = withJ $ peekNextQuux >>= liftIO . print
Now I want to peek and print quuxes inside separate thread inside J
context
g = withJ . liftIO . forkIO . forever $ peekNextQuux >>= liftIO . print
Which obviously won't work. I guess there is some way to solve such a simple problem, just can't figure it out.
I'm not sure if this is what you need, but it sounds like you are looking for a function
forkJ :: J () -> J ThreadId
which is similar to forkIO, but works in J context instead. Generally speaking all of dflemstr's points are valid. There are many unresolved questions about state management due to Haskell's purity.
However, if you're willing to restructure your logic a little bit, one option that may work for you (if all you're looking for is a separate thread with access to the original state when you issued the fork) is the lifted-base pakcage, which depends on monad-control. It will essentially give you the forkJ function above as long as you have IO at the bottom of your transformer stack.
Now, if you want the 2 threads to communicate in a stateful manner, such that errors raised in the child are propagated to the main thread as part of the ErrorT machinery, this is just not possible (as dflemstr explained). You can, however, establish a channel of communication between the 2 threads using a construct from the Control.Concurrent module family. One of the following modules may have what you need:
Control.Concurrent.Chan
Control.Concurrent.MVar
Control.Concurrent.STM
How do you expect it to work? The separate thread has to have access to some state and some error handling, because J
wraps StateT
and ErrorT
. How should the thread get access to this? When the state is updated in the new thread, should it be changed in the old thread too? When the new thread throws an exception, should the old thread halt?
It cannot work, because StateT
and ErrorT
are pure monad transformers, so the behaviors I described are not possible to implement. You must explicitly pass the state to the new thread and run a new state monad there for it to work:
g = withJ . ... $ do
state <- get
liftIO . forkIO $ do
flip execStateT state . forever $ peekNextQuux >>= liftIO . print
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With