在应用函数monad之前对输入执行转换

时间:2017-09-24 18:03:09

标签: haskell lambda monads

我知道我可以使用函数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参数。有没有更好的方法来实现这样的结构?

3 个答案:

答案 0 :(得分:5)

由于所有Monad个实例也有Functor个实例,而Functor的函数实例都有fmap = (.),因此您可以拥有

compute :: String -> String 
compute = flip fmap (++ "abcd") $ do
    x   <- length
    top <- head
    return $ replicate x top

某些软件包(例如microlenslens)定义(<&>) = 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)

应该有效(目前无法测试)。