减少 happstack 教程代码中的冗余

发布于 2024-12-01 00:29:44 字数 1006 浏览 1 评论 0原文

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"),还是根本不可能?

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

The happstack tutorial provides the following sample:

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"
       ]

It seems that ok $ is redundant here -- is there any way of pulling that out of msum so that you don't have to write ok $ three times? I tried the following, but it doesn't even compile:

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"
       ]

Is there a correct way to do this (or even better, pulling out the entirety of ok $ "You did a " and ".\n"), or is it just not possible?

I'm still getting up to speed on how monads work in Haskell, but if the above is not possible, then can you explain from a high level why there's no reasonable way to make this work, or what would need to be changed in order to allow it to be possible? I'm just trying to wrap my head around what can and cannot be done here.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

ok 并不是真正多余。

让我们仔细看看其中一个 do 块。我们将第一个 do 块拆分为一个名为 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"

因为 do 块中的最后一行的类型为 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,这意味着应用诸如 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!"
       ]

希望这有帮助!

The ok is not really redundant.

Let's look at one of the do blocks up close. We will split the first do-block out into a separate function named getPart.

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

So, we see clearly that we are working with the ServerPart monad. Therefore every line in the do block must have a type like ServerPart a.

Writing something like this won't work:

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

because the last line in that do block has the type String not the require ServerPart String. The typical way to convert a String to ServerPart String is by using return:

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

Remember that return has the type:

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

But, of course, that is not any better than what we had before. Instead of ok we have return. There is really no way to avoid that 'boilerplate'. You need a ServerPart String not a String and that means applying a function like return or ok to do the lifting.

As you note, the "You did a " part of the message is redundant. There are several ways we could deal with that. We could have the handlers just return the part of the message that is different like this:

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"
       ]

And then we can get that String and add the rest of the message:

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

(This can be expressed more compactly, but I am aiming for readability here).

One problem with that solution is that it forces all those handlers to conform to the exact same mold. If we wanted to add a handler that returned a message that did not fit that pattern, we would be in trouble. Another option would be to create a simple helper function that encapsulates that pattern:

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!"
       ]

Hope this helps!

幻梦 2024-12-08 00:29:44

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

Not sure about the type of dir, but something like this should work:

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")

With such short blocks, I'd be tempted to un-do them:

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")
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文