As a relatively new Haskeller, I’ve been mystified at the
lift family of functions.
They idea that they promote a pure function to do some computation in a monad makes sense, but I’ve never found uses for them in my code, and I think it’s because I don’t fully understand them.
That changed yesterday, so I hope that my process will help other people in my position come to terms with
(Beware: there are probably lots of technically incorrect ways to describe monads and their operations below! I’d appreciate being corrected on them!)
I recently wrote a toy program that had to read matrices from user input. I first wrote it using this pattern:
str <- getLine let mat = readMatrix str
getLine is from the Prelude (and has type
readMatrix is from hmatrix.
Its purpose is to read a
String and construct a
Matrix Double from it - which is represented by the type
String -> Matrix Double.
So I’m sure anybody reading this is fairly clear on why the code does what it does.
strMat <- getLine “unboxes” the value of
getLine, allowing us to use it as a pure
String rather than an
IO String, so we can give it to
readMatrix to get a pure matrix value.
This pattern annoyed me slightly, since I would never make use of
I knew that, this being Haskell, there must me some way to simplify the two-line pair into a single line (or probably a single function call with lots of punctuation!).
Fetching a value in a monad and operating on it with a pure function sounded like it should be a fairly common task.
The first step was to get rid of the
do notation and see the actual underlying function calls.
According to the wiki, the bind operator (
>>=) is the result of desugaring the
Bind takes a monadic value on the left-hand side, “unboxes” it, and feeds it to a monadic function on the right-hand side.
So I could rewrite my two lines as one line with a bind:
mat <- getLine >>= \x -> return (readMatrix x)
Since binding is a monadic operation, you can’t just bind
getLine directly to a pure function (like,
getLine >>= readMatrix).
The whole expression must return some monadic value, which we then purify using the
<- notation after the bind has happened.
So I managed to condense my two lines into one line, but it’s still fairly ugly, with all that line noise in there. We can at least get rid of the lambda by using a pointfree style:
mat <- getLine >>= return . readMatrix
But this still smelled like boilerplate. Surely, I told myself, surely people need to perform some impure/monadic function, pass it to a pure function, and get an impure result back which they can purify by binding it. In fact, I could write a little utility function for it myself:
doSomething impureFn pureFn = impureFn >>= return . pureFn
And now, I could rewrite my input line like this:
mat <- doSomething getLine readMatrix
Suspecting that this function may already exist, I set off to Hoogle to find it. I discovered the type of my new function using GHCi:
ghci> :t doSomething doSomething :: (Monad m) => m a1 -> (a1 -> r) -> m r
mat <- liftM readMatrix getLine
Which is about as concise as I could ask for.
So, if you ever need to perform a pure operation on a monadic value, try
lifting your function! Here are the original and the final versions side-by-side:
-- Explicit strMat <- getLine let mat = readMatrix strMat -- Lifted mat <- liftM readMatrix getLine