Рефакторинг функции Haskell, использующей монаду Reader
У меня есть код, который выглядит примерно так, игнорируя весь код, который не имеет отношения к моему вопросу:
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'
Есть ли способ реорганизовать это? В частности, вложенные bar'
функция беспокоит меня больше всего. Может ли это быть сжато до одной строки?
1 ответ
Мы можем сделать несколько объективных рассуждений. Сначала давайте посмотрим на bar'
, Я напишу это в таком виде
asks bar >>= \z -> return (z i)
Оказывается, что liftM
определяется как liftM f m = m >>= \a -> return (f a)
что соответствует шаблону выше. Итак, давайте заменим его
liftM ($ i) (asks bar)
Тогда у нас есть foo
как существо
liftM show (liftM ($ i) (asks bar))
Или выписано немного особо
liftM show . liftM ($ i) $ asks bar
Если мы знаем, что liftM
является fmap
мы могли бы признать Functor
закон здесь играет
fmap show . fmap ($ i) $ asks bar -- equals
fmap (show . ($ i)) $ asks bar
Я лично не большой поклонник использования ($ i)
как функция, так что давайте перепишем его как явную лямбду
fmap (\f -> show (f i)) (asks bar)
Теперь мы можем решить устранить asks
используя bar
на сайте вызова (т.е. использовать bar
как функция типа bar :: FooEnv -> Int -> Int
fmap (\f -> show (bar f i)) ask
и как последний трюк, мы могли бы использовать flip
идти бессмысленно в fmap
Пед функции и даже вернуть использование asks
(спасибо Эрджан Йохансен)
fmap (show . flip bar i) ask -- or even
show . flip bar i <$> ask -- or even
asks (show . flip bar i)
Я не говорю, что это самый читаемый или замечательный способ выполнения этой задачи, но вы можете видеть, как мы можем просто свести на нет кусочки, используя рациональные рассуждения.