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 $は冗長なようです.3回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")、または単に不可能ですか?

私はまだ Haskell でモナドがどのように機能するかについて理解を深めていますが、上記が不可能な場合は、なぜこれを機能させる合理的な方法がないのか、または何を変更する必要があるのか​​を大まかに説明していただけますか?可能にするには?ここでできることとできないことについて頭を抱えようとしているだけです。

4

2 に答える 2

5

ok実際には冗長ではありません。

do ブロックの 1 つを間近で見てみましょう。最初の 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が require ではないためですServerPart StringStringaをに変換する一般的な方法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")

(これはもっとコンパクトに表現できますが、ここでは読みやすさを目指しています)。

このソリューションの問題点の 1 つは、すべてのハンドラーをまったく同じ型に合わせることを強制することです。そのパターンに適合しないメッセージを返すハンドラーを追加したい場合、問題が発生します。別のオプションは、そのパターンをカプセル化する単純なヘルパー関数を作成することです。

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 に答える