Haskell: When not to use "Do" notation
I recently suggested a change to the Gitit wiki so that it returned a 404 Not Found code rather than a 200 OK when a user tried to access a page that doesn’t exist.
Just returning a 404 code would break Gitit as there would be no way to create a new page so I wanted everything to be the same apart from the status code.
In Gitit the functions that do that page content are called Handlers and they have type
type Handler = GititServerPart Response
The function notFound that returns the 404 status code has type
notFound :: (FilterMonad Response m) => a -> m a
So in order to use the handler as an argument for notFound I had to lift (is lift the right word here?) it out of the GititServerPart monad. My solution was as follows:
handler404::Handler->Handler
handler404 handler = do
handler'<-handler
notFound $ handler'
I emailed this change to the Gitit-Discuss mailing list and once they realised it wouldn’t break everything John MacFarlane made some changes. When I looked at then I saw that this is what he had done (the “guardPath isPage >> createPage” part is a Handler):
notFound =<< (guardPath isPage >> createPage)
As you can see, my code is a textbook example of when using “do” notation is a bad idea.
Summary
Suppose you want to apply the function bar
bar::a->m a
to foo
type foo = m a
If this is all you want to do then do not use “do” notation; using arrows like “=<<” makes the code more concise and much easier to follow.
