4

In the following code I am trying to combine 2 Producers into 1. All have the same type. They will be combined by each of the 2 input Producers being run in a separate thread and being consumed by a Consumer that puts the values into an unagi chan (I'm using unagi chan for performance). A producer is returned which reads from the chan. I want to be able to take any Producer as long as it can run IO so I restrict the class of the monad to MonadIO and HasFork.

import Pipes (Producer, Consumer, (>->), yield, await, runEffect)
import Control.Concurrent.Chan.Unagi (InChan, OutChan, newChan, readChan, writeChan)
import Control.Monad (forever, void)
import Control.Concurrent.MonadIO (MonadIO, HasFork, liftIO, fork)

combine :: (MonadIO m, HasFork m) => Producer a m r -> Producer a m r -> Producer a m r
combine p1 p2 = do
  (inChan, outChan) <- liftIO $ newChan
  t1 <- fork . void . runEffect $ p1 >-> (consumer inChan)
  t2 <- fork . void . runEffect $ p2 >-> (consumer inChan)
  producer outChan

producer :: (MonadIO m) => OutChan a -> Producer a m r
producer outChan = forever $ do
  msg <- liftIO $ readChan outChan
  yield msg

consumer :: (MonadIO m) => InChan a -> Consumer a m r
consumer inChan = forever $ do
  msg <- await
  liftIO $ writeChan inChan msg

However I get the following error:

    • Couldn't match type ‘m’
                 with ‘Pipes.Internal.Proxy Pipes.Internal.X () () a m’
  ‘m’ is a rigid type variable bound by
    the type signature for:
      combine :: forall (m :: * -> *) a r.
                 (MonadIO m, HasFork m) =>
                 Producer a m r -> Producer a m r -> Producer a m r
    at src/Pipes/Unagi.hs:8:12
  Expected type: Pipes.Internal.Proxy
                   Pipes.Internal.X () () a m GHC.Conc.Sync.ThreadId
    Actual type: m GHC.Conc.Sync.ThreadId

Is what I'm trying to do possible?

shmish111
  • 3,697
  • 5
  • 30
  • 52
  • Add `lift` in front of `fork`. `fork stuff :: m ThreadId`, `lift :: m r -> Producer a m r`. Although I don't think there are a lot of interesting types that you can actually fork. – Li-yao Xia Sep 09 '17 at 00:25
  • Sweet it worked. Can you explain why it works? Maybe you should post an answer so I can mark it as correct and you get more points? – shmish111 Sep 09 '17 at 11:28
  • Also, the reason I want to use `fork` is so that I can use any monad stack as long as it has IO. It doesn't matter that `IO` is about the only type you can `fork` – shmish111 Sep 09 '17 at 11:29

1 Answers1

2

Add lift in front of fork.

t1 <- lift . fork . ...

In your function, fork . ... has type m ThreadId, but the do block is in the monad Producer a m.

Also, lifted-base has a more up to date fork.

Li-yao Xia
  • 31,896
  • 2
  • 33
  • 56
  • Ah yes, originally I tried to do this with lifted-async but I couldn't work it out. I couldn't find any information on how I 'run' a `MonadBase`. Or how do I run `(MonadIO m, MonadBase m) => Producer a m r`? – shmish111 Sep 09 '17 at 12:47
  • `MonadBaseControl IO m` is just like `HasFork m`. There is no "run" function to apply. – Li-yao Xia Sep 09 '17 at 13:09