【问题标题】:Refactoring a Haskell function that uses the Reader monad重构使用 Reader monad 的 Haskell 函数
【发布时间】:2014-09-08 04:06:58
【问题描述】:

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

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

【问题讨论】:

    标签: haskell refactoring monads do-notation reader-monad


    【解决方案1】:

    我们可以做一些等式推理。首先让我们看看bar'。我就这样写吧

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

    事实证明,liftM 被定义为符合上述模式的liftM f m = m &gt;&gt;= \a -&gt; 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)
    

    现在,我们可以决定通过在调用站点使用bar 来消除asks(即使用bar 作为bar :: FooEnv -&gt; Int -&gt; 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)
    

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

    【讨论】:

    • 请注意asks f 等同于f &lt;$&gt; ask,所以根据您的口味,您可以在最后重新引入它:asks $ show . flip bar i
    • 谢谢,@j-abrahamson!详细的步骤很有帮助。不过,我应该更具体一点,因为我不想将bar' 内联到foo。不过,我会努力了解您采取的步骤,以便我可以尝试自己创建bar' 的免费版本。我更喜欢asks 而不是ask,所以也感谢@Ørjan-johansen 添加最后一步。
    • 我已经弄清楚了:bar' i' = asks $ flip bar i'bar' = asks . flip bar
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-17
    • 1970-01-01
    • 2016-07-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多