Haskell Refactoring Using Reader Monad

I have some code that looks something like this, ignoring all the code that is not relevant to my question:

import qualified Control.Monad.Reader as Reader data FooEnv = FooEnv { bar :: Int -> Int } type FooReader = Reader.Reader FooEnv foo :: Int -> FooReader String foo i = Reader.liftM show $ bar' i where bar' i' = do bar'' <- Reader.asks bar return $ bar'' i' 

Is there any way to reorganize this? In particular, the nested function bar' bothers me more. Can this be condensed in one line?

+6
source share
1 answer

We can do some equational reasoning. First, consider bar' . I will write it in this form

 asks bar >>= \z -> return (zi) 

It turns out that liftM is defined as liftM fm = m >>= \a -> return (fa) , which matches the pattern above. Therefore replace it with

 liftM ($ i) (asks bar) 

Then we have foo as

 liftM show (liftM ($ i) (asks bar)) 

Or, written out a bit especially

 liftM show . liftM ($ i) $ asks bar 

If we know that liftM is fmap , we can recognize the liftM law when playing here

 fmap show . fmap ($ i) $ asks bar -- equals fmap (show . ($ i)) $ asks bar 

I personally am not a big fan of using ($ i) as a function, so let it rewrite it as an explicit lambda

 fmap (\f -> show (fi)) (asks bar) 

Now we can decide to exclude asks using bar on the call site (i.e. use bar as a function of type bar :: FooEnv -> Int -> Int

 fmap (\f -> show (bar fi)) ask 

and as a final trick we could use flip for the meaningless function of the fmap pedals and even return the use of asks (thanks Ørjan Johansen)

 fmap (show . flip bar i) ask -- or even show . flip bar i <$> ask -- or even asks (show . flip bar i) 

I am not saying that this is the most readable or wonderful way to accomplish this task, but you can see how we can simply destroy the pieces with equational reasoning.

+9
source

Source: https://habr.com/ru/post/974958/


All Articles