
On Sun, Mar 3, 2013 at 10:28 AM, Martin Drautzburg 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.