我有一些看起来像这样的代码,忽略了与我的问题无关的所有代码:
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'
函数最让我困扰。这可以浓缩成一行吗?
答案 0 :(得分: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)
作为函数的忠实粉丝,所以让我们将其重写为显式lambda
fmap (\f -> show (f i)) (asks bar)
现在,我们可以决定在呼叫网站上使用asks
消除bar
(即使用bar
作为bar :: FooEnv -> Int -> Int
类型的函数
fmap (\f -> show (bar f i)) ask
作为最后一招,我们可以使用flip
在fmap
ped函数中毫无意义,甚至返回使用asks
(感谢ØrjanJohansen)
fmap (show . flip bar i) ask -- or even
show . flip bar i <$> ask -- or even
asks (show . flip bar i)
我并不是说这是执行此任务的最可读或最好的方式,但你可以看到我们如何使用等式推理来减少碎片。