私は、巨大な JSON ドキュメントを受け取り、それを断片的に解析し、断片ごとにエラー メッセージを報告することになっている Web API の次の小さなミニ サンプル アプリケーションを持っています。
次のコードは、EitherT (およびエラー パッケージ) を使用した実際の例です。ただし、問題は、EitherT が最初に検出された Left で計算を中断し、最初に検出した「エラー」を返すことです。私が望むのは、生成可能なすべてのエラー メッセージのリストです。たとえば、最初の行がrunEitherT
失敗した場合、それ以上できることはありません。しかし、2 行目が失敗した場合でも、2 行目にはデータの依存関係がないため、後続の行を実行しようとすることができます。したがって、理論的には、一度に多くの(必ずしもすべてではない) エラー メッセージを生成できます。
すべての計算を遅延して実行し、見つけたすべてのエラー メッセージを返すことは可能ですか?
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.ByteString.Lazy.Char8 (pack)
import Web.Scotty as S
import Network.Wai.Middleware.RequestLogger
import Data.Aeson
import Data.Aeson.Types
import Control.Lens hiding ((.=), (??))
import Data.Aeson.Lens
import qualified Data.Text as T
import Control.Error
import Control.Applicative
import qualified Data.HashMap.Strict as H
import Network.HTTP.Types
data TypeOne = TypeOne T.Text TypeTwo TypeThree
deriving (Show)
data TypeTwo = TypeTwo Double
deriving (Show)
data TypeThree = TypeThree Double
deriving (Show)
main :: IO ()
main = scotty 3000 $ do
middleware logStdoutDev
post "/pdor" $ do
api_key <- param "api_key"
input <- param "input"
typeOne <- runEitherT $ do
result <- (decode (pack input) :: Maybe Value) ?? "Could not parse. Input JSON document is malformed"
typeTwoObj <- (result ^? key "typeTwo") ?? "Could not find key typeTwo in JSON document."
typeThreeObj <- (result ^? key "typeThree") ?? "Could not find key typeThree in JSON document."
name <- (result ^? key "name" . _String) ?? "Could not find key name in JSON document."
typeTwo <- hoistEither $ prependLeft "Error when parsing TypeTwo: " $ parseEither jsonTypeTwo typeTwoObj
typeThree <- hoistEither $ prependLeft "Error when parsing TypeThree: " $ parseEither jsonTypeThree typeThreeObj
return $ TypeOne name typeTwo typeThree
case typeOne of
Left errorMsg -> do
_ <- status badRequest400
S.json $ object ["error" .= errorMsg]
Right _ ->
-- do something with the parsed Haskell type
S.json $ object ["api_key" .= (api_key :: String), "message" .= ("success" :: String)]
prependLeft :: String -> Either String a -> Either String a
prependLeft msg (Left s) = Left (msg ++ s)
prependLeft _ x = x
jsonTypeTwo :: Value -> Parser TypeTwo
jsonTypeTwo (Object v) = TypeTwo <$> v .: "val"
jsonTypeTwo _ = fail $ "no data present for TypeTwo"
jsonTypeThree :: Value -> Parser TypeThree
jsonTypeThree (Object v) = TypeThree <$> v .: "val"
jsonTypeThree _ = fail $ "no data present for TypeThree"
誰かがいくつか持っている場合は、リファクタリングの提案にもオープンです。