aru*_*l84 6 monads refactoring haskell do-notation reader-monad
我有一些看起来像这样的代码,忽略了与我的问题无关的所有代码:
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'
Run Code Online (Sandbox Code Playgroud)
有没有办法重构这个?具体来说,嵌套bar'
函数最让我困扰.这可以浓缩成一行吗?
我们可以做一点均等推理.首先让我们来看看bar'
.我会用这种形式写的
asks bar >>= \z -> return (z i)
Run Code Online (Sandbox Code Playgroud)
事实证明,这liftM
被定义为liftM f m = m >>= \a -> return (f a)
适合上述模式.所以让我们替换它
liftM ($ i) (asks bar)
Run Code Online (Sandbox Code Playgroud)
然后我们foo
就是这样
liftM show (liftM ($ i) (asks bar))
Run Code Online (Sandbox Code Playgroud)
或者,特别写出来
liftM show . liftM ($ i) $ asks bar
Run Code Online (Sandbox Code Playgroud)
如果我们知道这liftM
是fmap
我们可能会认识到Functor
这里的法律
fmap show . fmap ($ i) $ asks bar -- equals
fmap (show . ($ i)) $ asks bar
Run Code Online (Sandbox Code Playgroud)
我个人并不是使用($ i)
函数的忠实粉丝,所以让我们将它重写为一个显式的lambda
fmap (\f -> show (f i)) (asks bar)
Run Code Online (Sandbox Code Playgroud)
现在,我们可以决定asks
通过bar
在呼叫站点使用来消除(即bar
作为类型的函数使用)bar :: FooEnv -> Int -> Int
fmap (\f -> show (bar f i)) ask
Run Code Online (Sandbox Code Playgroud)
作为最后一招,我们可以使用ped功能flip
毫无意义fmap
甚至返回使用asks
(感谢ØrjanJohansen)
fmap (show . flip bar i) ask -- or even
show . flip bar i <$> ask -- or even
asks (show . flip bar i)
Run Code Online (Sandbox Code Playgroud)
我不是说这是执行此任务的最可读或最好的方式,但您可以看到我们如何使用等式推理来减少碎片.