Question

Consider the following code (and error):

import Control.Lens
import Control.Monad.STM
import Control.Monad.IO.Class
import Control.Monad.State.Lazy
import Control.Concurrent.STM.TChan

data Broadcast = Broadcast Int

data ImmutableState = ImmutableState
    { _broadcastChan :: TChan Broadcast
    , _encryption    :: Int
    }

makeLenses ''ImmutableState

helper :: MonadIO m => StateT ImmutableState m a
helper = do
    broadcastChan <~ (liftIO . atomically $ dupTChan $ use broadcastChan)
    {- ^
        Test.hs:
            Couldn't match expected type `Broadcast'
                        with actual type `TChan Broadcast'
            Expected type: StateT ImmutableState m (TChan Broadcast)
              Actual type: StateT ImmutableState m (TChan (TChan Broadcast))
            In the second argument of `(<~)', namely
              `(liftIO . atomically $ dupTChan $ use broadcastChan)'
            In a stmt of a 'do' block:
              broadcastChan
              <~ (liftIO . atomically $ dupTChan $ use broadcastChan)
    -}

forkImmState :: MonadIO m => ImmutableState -> m ImmutableState
forkImmState s = evalStateT helper s

Can somebody explain how (TChan (TChan Broadcast)) comes to be?

Was it helpful?

Solution

dupTChan expects an argument like TChan a and is being applied something like MonadState ImmutableState m => m (TChan Broadcast). If you just try that directly you'll get a slightly different kind of type error

*Main> :t dupTChan $ use broadcastChan

<interactive>:1:12:
    No instance for (MonadState ImmutableState TChan)
      arising from a use of `use'
    Possible fix:
      add an instance declaration for (MonadState ImmutableState TChan)
    In the second argument of `($)', namely `use broadcastChan'
    In the expression: dupTChan $ use broadcastChan

where GHC tries to unify TChan a and m (TChan Broadcast) by having a ~ TChan Broadcast and m ~ TChan, but it fails because TChan isn't an instance of MoandState. You can already see how the TChan (TChan Broadcast) error is forming, but it can't unify so it stops.

It's only when we claim we know the type of the result that GHC tells us what it's trying behind the scenes

*Main> :t (dupTChan $ use broadcastChan) :: STM (TChan Broadcast)

<interactive>:1:2:
    Couldn't match type `TChan Broadcast' with `Broadcast'
    Expected type: STM (TChan Broadcast)
      Actual type: STM (TChan (TChan Broadcast))
    In the expression:
        (dupTChan $ use broadcastChan) :: STM (TChan Broadcast)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top