This is a quick hack, inspired by Liyang Hu's talk at AngloHaskell about "idiom brackets". It allows you to write code like this: res :: IO () res = print =<< $(translate [| (run readInt - run readInt) + run readInt |]) readInt :: IO Int readInt = do l <- getLine return (read l) Examples like $(translate [| twice (\n -> n + run readInt) 0 |]) will only do a single readInt, while $(translate [| (\n -> run (readInts n)) 3|]) where readInts :: Int -> IO [Int] readInts n = sequence (replicate n readInt) will lift the use of n out of scope and break completely. let is not handled at all. Code which needs explicit type signatures to get past the type checker will have them stripped out and will thus break. Instances of run inside branches of if and case statements will be executed no matter which branch is actually taken. I think it might be quite interesting to combine this with "Scrap Your Boilerplate" to allow people to define their own translation strategies.