<div dir="ltr">TLDR: New forkable monad/transformer suggestion <a href="http://pastebin.com/QNUVL12v">http://pastebin.com/QNUVL12v</a> (hpaste is down)<br><br>Hi,<br><br>There are a dozen packages on hackage defining a class for monads that can be forked, however none of these are modular enough to be useful in my opinion.<br>
<br>In particular the following are not addressed:<br>1. Cases when the child thread&#39;s monad is different from the parent&#39;s<br>2. Monad transformers (this is somewhat addressed with Control.Monad.Trans.Control)<br>
<br>I will try to demonstrate both issues with an example.<br><br>1. WebSockets<br><br>WebSockets is a monad that cannot itself be forked. This is because at any given time there should only be a single thread listening on a websocket.<br>
However there is a reasonable monad that can be forked off, namely one that can send to the websocket - one that has access to the Sink.<br><br>So first off a &quot;Forkable&quot; class should not look like this:<br><br>class (MonadIO m, MonadIO n) =&gt; Forkable m where<br>
    fork :: m () -&gt; m ThreadId<br><br>But rather like this:<br><br>class Forkable m n where<br>    fork :: n () -&gt; m ThreadId<br><br>For our example the instance would be<br><br>instance (Protocol p) =&gt; Forkable (WebSockets p) (ReaderT (Sink p) IO) where<br>
    fork (ReaderT f) = liftIO . forkIO . f =&lt;&lt; getSink<br><br>Another example would be a child that should not be able to throw errors as opposed to the parent thread.<br><br>2. ReaderT<br><br>Continuing from the previous example to demonstrate the need to distinguish forkable transformers.<br>
Say we have some shared state S that both parent and child should have access to:<br><br>type Parent p = ReaderT (TVar S) (WebSockets p)<br>type Child p = ReaderT (TVar S) (ReaderT (Sink p) IO)<br><br>The &quot;forkability&quot; of Child from Parent should be implied, however with Forkable we have to write a separate instance.<br>
<br>So what I suggest is a second class:<br><br>class ForkableT t where<br>    forkT :: (Forkable m n) =&gt; t n () -&gt; t m ThreadId<br><br>And then:<br><br>instance ForkableT (ReaderT r) where<br>    forkT (ReaderT f) = ReaderT $ fork . f<br>
<br>We can also introduce a default for Forkable that uses a ForkableT instance:<br><br>class (MonadIO m, MonadIO n) =&gt; Forkable m n where<br>    fork :: n () -&gt; m ThreadId<br>    default fork :: ForkableT t =&gt; t n () -&gt; t m ThreadId<br>
    fork = forkT<br><br>instance (Forkable m n) =&gt; Forkable (ReaderT r m) (ReaderT r n)<br><br>This means Child is automatically Forkable from Parent, no need to write a specific case for our specific monads (and if we newtype it we can use -XGeneralizedNewtypeDeriving)<br>
<br>Note how MonadTransControl already solves the specific problem of lifting a forking operation into ReaderT. However consider ResourceT from Control.Monad.Resource: it is basically a ReaderT, however in order to safely deallocate resources when sharing reference counting is needed. This means a simple lift would not suffice.<br>
<br>We can nevertheless provide a default ForkableT based on MonadTransControl:<br>class ForkableT t where<br>    forkT :: (Forkable m n) =&gt; t n () -&gt; t m ThreadId<br>    default forkT :: (MonadTransControl t, Forkable m n) =&gt; t n () -&gt; t m ThreadId<br>
    forkT t = liftWith $ \run -&gt; fork $ run t &gt;&gt; return ()<br><br>Actually resourcet&#39;s reference counting resourceForkIO also nicely demonstrates the first problem:<br>type Parent p = ResourceT (WebSockets p)<br>
type Child p = ResourceT (ReaderT (Sink p) IO)<br><br>Note how we cannot use resourceForkIO without touching the underlying monads.<br><br>What do you think? Is there already an established way of modular forking? I wouldn&#39;t like to litter hackage with another unusable Forkable class:)<br>
<br></div>