Search code examples
haskellcompositionaesonmonadplus

Flatten MonadPlus inside an Aeson Parser


I'm not sure if I'm barking up the wrong tree here, but I have an Aeson FromJSON definition that looks rather bulky and I was wondering if it could be turned into something more concise. I want to short-circuit the parsing of the entire object if the nested parsing of the URI fails.

data Link = Link { link :: URI
                 , tags :: [String]
                 } deriving (Show, Typeable, Eq)

instance FromJSON Link where
    parseJSON :: Value -> Parser Link
    parseJSON (Object o) = do
        linkStr <- o .: "link"
        tags' <- o .: "tags"

        case parseURI linkStr of
            Just l -> return $ Link l tags'
            Nothing -> mzero

    parseJSON _ = mzero

The type of parseURI is parseURI :: String -> Maybe URI and both Maybe and Parser have MonadPlus instances. Is there a way to compose the two directly and remove the ugly case statement at the end?


Solution

  • Applicative parsers are usually more concise and you can compose the result of parseURI using maybe mzero return which converts a Nothing into an mzero.

    instance FromJSON Link where
        parseJSON :: Value -> Parser Link
        parseJSON (Object o) = Link
            <$> (maybe mzero return . parseURI =<< o .: "link")
            <*> o .: "tags"
    
        parseJSON _ = mzero