
On Sun, Nov 7, 2010 at 7:40 PM, Mitar
I have a class Neuron which has (among others) two functions: attach and deattach. I would like to make a way to call a list/stack/bunch of attach functions in a way that if any of those fail (by exception), deattach for previously already attached values (called attach on them) are deattached (called deattach on them).
Perhaps I'm misunderstanding how this works, but it seems like this could all be done fairly simply using standard combinators in Control.Monad.
growNeurons :: [IO Growable] -> IO [Growable] growNeurons attaches = growNeurons' attaches [] where growNeurons' [] ls = return ls growNeurons' (a:ats) ls = bracketOnError a (\(Growable l) -> deattach l) (\l -> growNeurons' ats (l:ls))
Isn't this mostly a reimplementation of mapM? Given a list of [IO Growable], you map over it to put a bracket around each one, then sequence the result (which I believe performs exactly the sort of nested monadic recursion you're doing here). I think that something like this ought to be equivalent: growNeurons :: [IO Growable] -> IO [Growable] growNeurons = mapM (\a -> bracketOnError a (\(Growable l) -> deattach l) return)
So I give growNeurons a list of attach actions and it returns a list of attached values ((live)neurons). This works nice, but syntax to use it is ugly:
neurons <- growNeurons [ do { a <- attach nerve1; return $ Growable a }, do { a <- attach nerve2; return $ Growable a }, do { a <- attach nerve3; return $ Growable a } ]
Along the lines of what Felipe suggested, this could possibly be simplified to something like: growNeurons $ map (fmap Growable . attach) [nerve1, nerve2, nerve3] ...except that this won't work if the nerves have different types. In many cases there's a trivial translation to get rid of existential types, and I suspect it would work here, but I'm not sure what else you might be doing with them. Existential types tend to be more trouble than they're worth, in my experience.
It seems to me that all this could be wrapped into a monad. So that I would be able to call something like:
neurons <- growNeurons' $ do attach nerve1 attach nerve2 attach nerve3
I'm not sure why you'd want to do it this way, relying on the monad to provide the sequencing. Isn't it more convenient to use a list of neurons, as above? - C.