
Your code examples are:
On Sat, Jun 27, 2009 at 6:07 PM, Edward Ing
saveFile n = do cont <- (liftM fromJust) $ getInputFPS "file" let f = uploadDir ++ "/" ++ basename n liftIO $ BS.writeFile f cont return $ paragraph << ("Saved as " +++ anchor ! [href f] << f +++ ".")
Vs.
saveFile n = do cont <- getInputFPS "file" let f = uploadDir ++ "/" ++ basename n liftIO $ BS.writeFile f (fromJust cont) return $ paragraph << ("Saved as " +++ anchor ! [href f] << f +++ ".")
Consider the line x <- y in a do expression. If y has type M a for some monad M, then x has type a. So, let's say you have a value f :: Maybe Int, and you want to return the Int's stringification if it exists. We can write this in these two ways: do x <- f return (show x) do x <- liftM show f return x liftM :: (a -> b) -> (M a -> M b) for any monad M. That means if you want to apply a function to a value which is currently wrapped in a monad constructor, you need to "lift" it in. liftM takes a function on ordinary values to a function on wrapped values. But *after* you bind, you don't need to lift anymore. Which of the two above styles to choose is a matter of style, and, in my code at least, varies from situation to situation. That said, you can write both of these snippets as "fmap show f" or "show <$> f" (where (<$>) is from Control.Applicative), which is how it would be done in practice. Does that make sense? Luke