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.