3

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 "and ".\n"),还是不可能?

我仍在了解 monads 在 Haskell 中的工作方式,但如果上述情况不可行,那么您能否从高层次上解释为什么没有合理的方法来完成这项工作,或者需要更改哪些内容让它成为可能?我只是想弄清楚在这里可以做什么和不能做什么。

4

2 回答 2

5

ok并不是真的多余。

让我们近距离看看其中一个 do 块。我们会将第一个 do-block 拆分为一个名为getPart.

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

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

写这样的东西是行不通的

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

因为该 do 块中的最后一行的类型String不是 require ServerPart StringString将 a 转换为的典型方法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. 真的没有办法避免那个'样板'。您需要 a ServerPart Stringnot a String,这意味着应用类似returnor的功能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!"
       ]

希望这可以帮助!

于 2011-08-23T02:20:01.643 回答
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")
于 2011-08-22T20:29:30.313 回答