Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

>>= implementation for Poor Man's Concurrency Monad

Tags:

haskell

monads

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?

like image 948
Jerry Yuan Avatar asked Nov 28 '14 14:11

Jerry Yuan


Video Answer


1 Answers

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))
like image 147
Stefan Holdermans Avatar answered Sep 21 '22 08:09

Stefan Holdermans