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'関数が最も気になります。これを1行に凝縮できますか?

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

それがわかっている場合liftM、ここで作用している法律をfmap認識する可能性がありますFunctor

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

私は個人的に($ i)関数として使用することの大ファンではないので、明示的なラムダとして書き直しましょう

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

ここで、呼び出しサイトでasks使用して を削除することを決定できます (つまり、型の関数として使用します)。barbarbar :: FooEnv -> Int -> Int

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

そして最後のトリックとして、 ped 関数でflip無意味になり、使用を返すことさえできます(thanks Ørjan Johansen)fmapasks

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 に答える