12

Hi I am trying to implement the Poor Man's Concurrency Monad. Here is my code:

import Control.Monad

data Concurrent a = Concurrent ((a -> Action) -> Action)

data Action 
    = Atom (IO Action)
    | Fork Action Action
    | Stop

instance Monad Concurrent where
    (Concurrent ma) >>= f = Concurrent (\x -> ma(\y -> "Something return a Action")) 
    return x = Concurrent (\c -> c x)

Here is my analysis: x has the type of b, y has the type of a, f has the type of (a -> ((b ->Action) -> Action)). In order to figure out "Something return a Action", I firstly calculate (f y), which returns a type of ((b ->Action) -> Action). Then, how can use it with x to generate a Action?

Thom Wiggers
  • 6,938
  • 1
  • 39
  • 65
Jerry Yuan
  • 685
  • 2
  • 6
  • 16
  • Your analysis is wrong. `x` has type `b -> Action`, not `b`. – bennofs Nov 28 '14 at 15:01
  • You might want to check out the [Free Monad](http://stackoverflow.com/questions/13352205/what-are-free-monads). It allows you to record a monad, which can be arbitrarily replayed. I used it for lightweight threads. – Franky Dec 02 '14 at 06:57

1 Answers1

21

The definition you are looking for reads something like

Concurrent h >>= k  =  Concurrent (\f -> h (\x -> runConcurrent (k x) f))

How did we get there? As always, we let the types do the work. :)

Let us first introduce a helper function:

runConcurrent                 :: Concurrent b -> (b -> Action) -> Action
runConcurrent (Concurrent h)  =  h

If you start out with the left-hand side of your definition

Concurrent h >>= k  =  ...

with h :: (a -> Action) -> Action and k :: a -> Concurrent b, then your goal is to replace ... with an expression of type Concurrent b, isn't it?

How can we construct a value of type Concurrent b? One way is to apply our function k, but that won't work, because we don't have a suitable value of type a available as an argument. So, pretty much the only thing we can do is to apply the data constructor Concurrent which is of type ((b -> Action) -> Action) -> Concurrent b.

That gives:

Concurrent h >>= k = Concurrent ...

Now we have to go and find us an expression of type (b -> Action) -> Action to supply as an argument for Concurrent. We know that expressions of function type can always be constructed through lambda-abstraction:

Concurrent h >>= k  =  Concurrent (\f -> ...)

This gives us f :: b -> Action and the obligation to replace ... with an expresion of type Action. Using one of the Action-constructors directly would be cheating of course ;). To guarantee the genericity of (>>=) (more precisely, to make sure that we end up obeying the monad laws), we treat Action as if it's an abstract datatype. Then, the only way to produce an Action-value is to apply the function h:

Concurrent h >>= k  =  Concurrent (\f -> h ...)

Hence, next we need to supply h with an argument of type a -> Action. That is a function type again, so we throw in another lambda:

Concurrent h >>= k  =  Concurrent (\f -> h (\x -> ...))

Hence, we have x :: a and need to construct a body of type Action. What can we do with a value of type a? We can supply it to the function k. This gives us a value of type Concurrent b, which we can then pass to our helper function runConcurrent:

Concurrent h >>= k  =  Concurrent (\f -> h (\x -> runConcurrent (k x) ...))

This gives us a function of type (b -> Action) -> Action and supplying f as an argument does the trick:

Concurrent h >>= k  =  Concurrent (\f -> h (\x -> runConcurrent (k x) f))
Stefan Holdermans
  • 7,990
  • 1
  • 26
  • 31
  • Little mistake type of the data constructor `Concurrent` is `((b -> Action) -> Action) -> Concurrent b` not `((b -> Action) -> b) -> Concurrent b`. Am I right ? – fdelsert Dec 01 '14 at 22:15
  • @fdelsert You're absolutely right. Thanks for spotting it. Corrected it in the answer. – Stefan Holdermans Dec 01 '14 at 22:40