减少happstack教程代码中的冗余



happstack教程提供了以下示例:

main :: IO ()
main = simpleHTTP nullConf $ msum 
       [ do methodM GET
            ok $ "You did a GET request.n"
       , do methodM POST
            ok $ "You did a POST request.n"
       , dir "foo" $ do methodM GET
                        ok $ "You did a GET request on /foo.n"
       ]

似乎ok $在这里是多余的——有没有办法把它从msum中抽出来,这样你就不必把ok $写三遍了?我尝试了以下操作,但它甚至无法编译:

main :: IO ()
main = simpleHTTP nullConf $ ok $ msum 
       [ do methodM GET
            "You did a GET request.n"
       , do methodM POST
            "You did a POST request.n"
       , dir "foo" $ do methodM GET
                        "You did a GET request on /foo.n"
       ]

是否有正确的方法来做到这一点(甚至更好,拔出全部ok $ "You did a "".n"),或者它只是不可能的?

我仍然在加速了解单子在Haskell中的工作方式,但如果上面是不可能的,那么你能从一个高层次解释为什么没有合理的方法来实现这个工作,或者需要改变什么才能使它成为可能吗?

ok并不是真的多余。

让我们仔细看看其中一个do块。我们将把第一个do块拆分为一个名为getPart的单独函数。

getPart :: ServerPart String
getPart = do methodM GET
             ok $ "You did a GET request.n"

所以,我们清楚地看到我们正在使用ServerPart单子。因此,do块中的每一行都必须具有类似ServerPart a的类型。

像这样写是行不通的:

getPart :: ServerPart String
getPart = do methodM GET
             "You did a GET request.n"

,因为do块的最后一行的类型是String而不是ServerPart String。将String转换为ServerPart String的典型方法是使用return:

getPart :: ServerPart String
getPart = do methodM GET
             return "You did a GET request.n"

请记住,return的类型是:

return :: (Monad m) => a -> m a

但是,当然,这并不比我们以前有什么好。我们用return代替ok。确实没有办法避免这种"样板文件"。您需要ServerPart String而不是String,这意味着应用returnok之类的函数来执行提升。

正如您注意到的,消息的"You did a "部分是冗余的。有几种方法可以解决这个问题。我们可以让处理程序只返回与消息不同的部分,如下所示:

handlers :: ServerPart String
handlers = 
       [ do methodM GET
            ok $ "GET request"
       , do methodM POST
            ok $ "POST request"
       , dir "foo" $ do methodM GET
                        ok $ "GET request on /foo"
       ]

然后我们可以得到String并添加消息的其余部分:

main :: IO ()
main = simpleHTTP nullConf $ do msg <- handlers
                                return ("You did a " ++ msg ++ ".n")

(这可以表达得更简洁,但我的目标是可读性)。

该解决方案的一个问题是,它强制所有这些处理程序遵循完全相同的模式。如果我们想添加一个返回不符合该模式的消息的处理程序,就会遇到麻烦。另一种选择是创建一个简单的辅助函数来封装该模式:

methodMsg :: Method -> String -> ServerPart String
methodMsg mthd msg = do methodM mthd
                        ok $ "You did a " ++ msg ++ ".n"
main :: IO ()
main = simpleHTTP nullConf $ msum 
       [ methodMsg GET  "GET request"
       , methodMsg POST "POST request"
       , dir "foo" $ methodMsg GET "GET request on /foo"
       -- the bar handler does not follow the pattern
       , dir "bar" $ ok $ "let's go to the bar!"
       ]

希望这对你有帮助!

不确定dir的类型,但是像这样的东西应该可以工作:

main :: IO ()
main = simpleHTTP nullConf $ msum 
       [ do methodM GET
            return "GET request"
       , do methodM POST
            return "POST request"
       , dir "foo" $ do methodM GET
                        return "GET request on /foo"
       ] >>= ok . (s -> "You did a " ++ s ++ ".n")

对于这样短的块,我很想取消它们:

main :: IO ()
main = simpleHTTP nullConf $ msum 
       [ methodM GET  >> return "GET request"
       , methodM POST >> return "POST request"
       , dir "foo" $ methodM GET >> return "GET request on /foo"
       ] >>= ok . (s -> "You did a " ++ s ++ ".n")

相关内容

  • 没有找到相关文章

最新更新