Babra Cunningham
Babra Cunningham

Reputation: 2967

Basic threading, interleaving threads confusion?

I am slightly confused by the interleave function given in this guide.

I have the following data type:

data M m r = Atomic (m (M m r)) | Done r

I've created a lifting function to take a m a, inserts the a inside Done, and then re-inserts Done a back into m (). This forms the Atomic structure:

atm :: Monad m => m a -> M m a
atm m = Atomic $ liftM Done m

I've made M m an instance of the Monad class (which pattern matches based on data constructor):

instance (Monad m) => Monad (M m) where
   return = Done
   (Atomic m) >>= f = Atomic liftM (>>= f) m
   (Done r) >>= f = f v

There's a simple implementation function which accesses nested values within the Atomic wrapper:

runThread :: Monad m => M m a -> m a
runThread (Atomic m) = m >>= runThread --Extract m and recursively pass to runThread
runThread (Done r) = return r --Return Done

Then, there is the following interleave function:

interleave :: Monad m => M m r -> M m r -> M m r
interleave (Atomic m1) (Atomic m2) = do
   next1 <- atm m1 --? 
   next2 <- atm m2 --? 
interleave next1 next2
interleave (Done _) t2 = interleave t2
interleave t1 (Done _) = interleave t1

My points of confusion are at next1 <- atm m1 and next2 <- atm m2.

As I understand it, all this is doing is taking m1 from the (Atomic m1) wrapper and reinserting it back into an Atomic wrapper? How is this operation interleaving?

Am I missing something basic here? The code works fine, so I'm sure it's due to my confusion.

The rest of the code:

threadOne :: M IO ()
threadOne = do 
  atm $ print 1

threadTwo :: M IO ()
threadTwo = do
  atm $ print 2

main = do
  runThread (interleave threadOne threadTwo)

Upvotes: 3

Views: 155

Answers (1)

kosmikus
kosmikus

Reputation: 19637

You are partially right. The code

next1 <- atm m1

takes the atomic action that the first thread starts with and inserts it into the merged/interleaved thread. What's returned from the call to atm is the continuation next1 of that first thread.

But next, we are taking an action from the second thread, by saying

next2 <- atm m2

So the merged thread ends up executing an action from the first thread, and then one from the second thread. Hence "interleaving". We then continue by calling interleave recursively on the two continuations via

interleave next1 next2

Upvotes: 2

Related Questions