
On Friday, 8. February 2013 08:26:38 Henk-Jan van Tuyl wrote:
On Wed, 06 Feb 2013 22:31:05 +0100, Martin Drautzburg
wrote: Can someone please walk me through it and possibly show ways to avoid the massive nesting.
dtz = do
SndSeq.withDefault SndSeq.Block $ \h -> do
Client.setName (h :: SndSeq.T SndSeq.DuplexMode) "Haskell-Melody" Port.withSimple h "out"
(Port.caps [Port.capRead, Port.capSubsRead, Port.capWrite]) (Port.types [Port.typeMidiGeneric, Port.typeApplication]) $ \p
-> do
Queue.with h $ \q -> do
c <- Client.getId h let me = Addr.Cons c p conn <- parseDestArgs h me ["20:0"] Queue.control h q Event.QueueStart Nothing Queue.control h q (Event.QueueTempo (Event.Tempo
10000000)) Nothing
return ()
I like to divide large functions into several smaller ones:
dtz = SndSeq.withDefault SndSeq.Block f1 where f1 h = do Client.setName (h :: SndSeq.T SndSeq.DuplexMode) "Haskell-Melody" Port.withSimple h "out" (Port.caps [Port.capRead, Port.capSubsRead, Port.capWrite]) (Port.types [Port.typeMidiGeneric, Port.typeApplication]) (f2 h) f2 h p = Queue.with h (f3 h p) f3 h p q = do c <- Client.getId h let me = Addr.Cons c p conn <- parseDestArgs h me ["20:0"] Queue.control h q Event.QueueStart Nothing Queue.control h q (Event.QueueTempo (Event.Tempo 10000000)) Nothing return ()
I tried to do exactly this and I like it a bit better. But note how those functions get quite a number of parameters. In the nested "do", evertything was in scope. Things get worse, when I try to run something in the innermost "do". When I use individual functions, I need yet another parameter which needs to travel through all these functions. As Brent Yorgey suggested, the nested "do"s can accept another parameter in the topmost function and it will be automatically in scope all the way down. I really wish, someone could elaboreate on this "$ \foo ->do" pattern, - when it is typically used, - what determines the depth of the nesting, - its pros and cons and the possible alternatives. It seems to be some kind of idiom, -- Martin