减少happstack教程代码中的冗余

时间:2011-08-22 20:14:51

标签: haskell 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"),或者这是不可能的?

我还在快速了解monads如何在Haskell中工作,但如果上述情况不可能,那么你能从高层解释为什么没有合理的方法来完成这项工作,或者需要做什么改变,以使其成为可能?我只想绕过这里可以做什么和不能做什么。

2 个答案:

答案 0 :(得分:5)

ok并非多余。

让我们近距离观察其中一个阻滞。我们将第一个do-block拆分为一个名为getPart的单独函数。

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

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

写这样的不会起作用

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

因为该块中的最后一行具有类型String而不是require 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

但是,当然,这并不比我们以前更好。我们ok取代return而不是ServerPart String。实际上没有办法避免这种“样板”。您需要String而不是return,这意味着应用ok"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!"
       ]

(这可以更紧凑地表达,但我的目标是可读性)。

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

{{1}}

希望这有帮助!

答案 1 :(得分:4)

不确定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")