我知道我可以使用函数monad来实现类似下面的构造(我在多个调用中重用参数而不显式引用它):
compute_v0 :: String -> String
compute_v0 = do
x <- length -- (using the argument implicitly here)
top <- head -- (and here)
return (replicate x top)
上述函数compute "1234"
的结果为"1111"
我的问题是:如何将变换应用于隐藏的&#39;执行do块之前的参数(想象一下我想将#34; abcd&#34;附加到列表中)。
我的第一个解决方案:
compute_v1 :: String -> String
compute_v1 = compute_v1' . (++ "abcd")
compute_v1' ::String -> String
compute_v1' = do
x <- length
top <- head
return (replicate x top)
compute "1234"
的结果现在为"11111111"
。这实际上完成了工作,但我宁愿尝试将其全部定义在一个简洁的代码块中。
我最接近实际包含转换的同时仍然保持代码(v0)的样式是这个:
compute_v2 :: String -> String
compute_v2 = (++ "abcd") >>= \r -> do
let x = length r
let top = head r
return $ replicate x top
但是我仍然必须包含一个lambda,使用很多let绑定并显式引用lambda参数。有没有更好的方法来实现这样的结构?
答案 0 :(得分:5)
由于所有Monad
个实例也有Functor
个实例,而Functor
的函数实例都有fmap = (.)
,因此您可以拥有
compute :: String -> String
compute = flip fmap (++ "abcd") $ do
x <- length
top <- head
return $ replicate x top
某些软件包(例如microlens
和lens
)定义(<&>) = flip fmap
,允许您编写
compute :: String -> String
compute = (++ "abcd") <&> do
x <- length
top <- head
return $ replicate x top
(->)
还有一个Category
个实例,它为我们提供了(>>>) = flip (.)
。这可能会在视觉上更加清晰:
compute :: String -> String
compute = (++ "abcd") >>> do
x <- length
top <- head
return $ replicate x top
答案 1 :(得分:4)
你可以这样做:
compute_v2 :: String -> String
compute_v2 = do
x <- length
top <- head
return $ replicate x top
<$> (++ "abcd")
AFAIK,有问题的monad被称为 Reader monad ,它也是Functor
。
*Q46393211> compute_v2 "1234"
"11111111"
*Q46393211> compute_v2 "71"
"777777"
答案 2 :(得分:1)
MonadReader
class为此设置了方法local
,(->) r
是一个实例,所以
import Control.Monad.Reader (local)
compute_v3 ::String -> String
compute_v3 = local (++ "abcd") $ do
x <- length
top <- head
return (replicate x top)
应该有效(目前无法测试)。