Add action without changing the result for do-notation refactoring

I want to consistently compose two monad actions in Haskell, discarding any value created by the second, and passing an argument to both actions. I am currently using do-block as follows:

ask = do result <- getLine putStrLn result return result 

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

 ask' = getLine <* putStrLn 

However, this does not even check the type, and the problem is that <* does not pass the result of the first action to the second. I want to associate actions like >>= , but not change the result. The type should be (a -> mb) -> (a -> mc) -> (a -> mb) , but Hoogle does not give suitable results. What would be the operator to achieve this functional composition?

+7
haskell refactoring pointfree do-notation
source share
3 answers

As a trend, if you use the same value in two different places, it is probably a good idea to give it a name in a transparent do block rather than click on a meaningless style.

The abstract concept of dividing the flow of information into different actions is captured by the Cartesian monoidal categories , known to the Haskellers as arrows . In your case, you mainly work 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.

+8
source share

I want to consistently compose two monad actions in Haskell, discarding any value created by the second, and passing an argument to both actions.

It sounds like Reader to me - the function type r -> ma is isomorphic to ReaderT rma , and the monad works by implicitly taking the same value of r into all the "holes". For example:

 import Control.Applicative import Control.Monad.Reader example :: IO String example = getLine >>= discarding putStrLn discarding :: Monad m => (a -> mb) -> a -> ma discarding action = runReaderT (ReaderT action *> ask) 

The statement you want is something like:

 action `thingy` extra = action >>= discarding extra 

But of course, discarding has a simpler implementation:

 discarding :: Applicative f => (a -> fb) -> a -> fa discarding action a = action a *> return a 

... therefore, in the end, I think it's really code golf. But in a more complex program, where it is a larger picture on a wider scale, it may be worth it. Basically, if you have:

 a0 :: r -> m a0 a1 :: r -> m a1 . . . an :: r -> m an 

It follows that:

 ReaderT a0 :: ReaderT rm a0 ReaderT a1 :: ReaderT rm a1 . . . ReaderT an :: ReaderT rm an 

And then:

 runReaderT (ReaderT a0 <* ReaderT a1 <* ... <* ReaderT an) :: r -> m a0 
+2
source share

For completeness in this particular case ( IO ) monad, you can also abuse the bracket for this purpose:

 bracket getLine putStrLn return 

But I strongly discourage him, since it will be much less readable than the original do notation block, it is just ugly.

As already mentioned, in this particular case, the best way is to name the result.

See also. Should Haskell be avoided?

+2
source share

All Articles