6

我有一些看起来像这样的代码,忽略了所有与我的问题无关的代码:

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'函数最困扰我。这可以浓缩成一行吗?

4

1 回答 1

9

我们可以做一些等式推理。首先让我们看一下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

如果我们知道那liftMfmap我们可能会认识到Functor这里的法律

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

我个人不太喜欢($ i)用作函数,所以让我们将其重写为显式 lambda

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

现在,我们可以决定asks通过bar在调用站点使用(即bar用作类型的函数)来消除bar :: FooEnv -> Int -> Int

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

作为最后一个技巧,我们可以flipfmapped 函数中使用变得毫无意义,甚至返回使用asks(感谢Ørjan Johansen)

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

我并不是说这是执行此任务的最易读或最美妙的方式,但您可以看到我们如何使用等式推理来减少碎片。

于 2014-09-08T04:30:58.273 回答