Рефакторинг функции 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)

Я не говорю, что это самый читаемый или замечательный способ выполнения этой задачи, но вы можете видеть, как мы можем просто свести на нет кусочки, используя рациональные рассуждения.

Другие вопросы по тегам