On Sun, Mar 3, 2013 at 10:28 AM, Martin Drautzburg <Martin.Drautzburg@web.de> wrote:
Hello all,

this was previously posted on Haskell Beginners, but only partially answered.

In Sound.ALSA.Sequencer, there are a number of functions which together set up
a midi environement (client, port, queue). They all have a type, where the
last argument has a type like:

(something.T -> IO a)

These things are in the Kleisli category for IO.  In short, an argument with this type is a function which makes an IO action.  The function which takes one of these as an action "knows" how to get a "something.T" to apply to the function, either because it is an argument to the bigger function, or because the library author knows the monad has an action with the type IO (something.T).

This is safer than passing around unconstrained IO actions.  For example, consider:

> outer :: String -> (Int -> IO ()) -> IO ()
versus
> outer :: String -> IO () -> IO ()

The second type requires that the library user can construct an appropriate IO () action, and the intended dependence on the Int is not statically verified.  On the other hand, the first type requires that you pass in an IO () action constructor that explicitly depends on an Int.  The "only" way you can drop the dependence on the Int is if you explicitly ignore it (and you can turn on warnings to catch that kind of thing)


i.e.

*Main> :t SndSeq.withDefault
SndSeq.withDefault
  :: SndSeq.OpenMode mode =>
     SndSeq.BlockMode -> (SndSeq.T mode -> IO a) -> IO a

*Main> :t Port.withSimple
Port.withSimple
  :: SndSeq.T mode
     -> String -> Port.Cap -> Port.Type -> (Port.T -> IO a) -> IO a

*Main> :t Queue.with
Queue.with :: SndSeq.T mode -> (Queue.T -> IO a) -> IO a

There is example code, where a full setup is created by a number of nested
"do" blocks. The repeating pattern there is:

something1 $ \x -> do
        something2 $ \y -> do
                something3 $ \z -> do


What is this all about? I particularly would like to understand, when this
parttern is needed and what determines the the number of nested "do" blocks.

It can be refactored, so it is never "needed".  On the other hand, it does have nice properties.  The x,y, z variables are all in scope when you're in something3's do-block argument.

The determining factor in the nesting depth is how many actions which take elements of a Kliesli category for the monad will be sequenced, since each one requires its own lambda-do-block.