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
,这意味着应用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!"
]
希望这对你有帮助!
不确定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")