Search code examples
haskellrefactoringpointfreedo-notation

Add action without changing result to refactor do-notation


I want to sequentially compose two monad actions in Haskell, discarding any value produced by the second, and passing the argument to both actions. Currently I'm using a do-block like this:

ask = do
  result <- getLine
  putStrLn result
  return result

I was hoping to write this a little more point free and neat, so I tried this:

ask' = getLine <* putStrLn

However, this doesn't even type check and the problem is that <* does not transfer the result of the first action to the second. I want to chain the actions like >>= does, but not change the result. The type should be (a -> m b) -> (a -> m c) -> (a -> m b), but Hoogle yields no suitable results. What would be an operator to achieve this function composition?


Solution

  • As a tendency, if you use one value in two different places it probably is a good idea to give it a name in a clear do block, rather than pressing on pointless style.

    The abstract concept of splitting up information flow to different actions is captured by cartesian monoidal categories, known to Haskellers as arrows. In your case, you're basically working in the IO Kleisli category:

    import Prelude hiding (id)
    import Control.Arrow
    
    ask' :: Kleisli IO () String
    ask' = Kleisli (\()->getLine) >>> (putStrLn &&& id) >>> arr snd
    

    I don't think it's a good idea to write such code.