在Aeson Parser中展平MonadPlus

时间:2015-03-19 21:17:32

标签: haskell composition aeson

我不确定我是否在这里咆哮错误的树,但我有一个看起来相当笨重的Aeson FromJSON定义,我想知道它是否可以变成更简洁的东西。如果URI的嵌套解析失败,我想缩短整个对象的解析。

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

parseURI的类型为parseURI :: String -> Maybe URIMaybeParser都有MonadPlus个实例。有没有办法直接组合这两个并在最后删除丑陋的case语句?

2 个答案:

答案 0 :(得分:4)

Applicative解析器通常更简洁,您可以使用parseURI撰写maybe mzero return的结果,将Nothing转换为mzero

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

    parseJSON _ = mzero

答案 1 :(得分:1)

模式匹配有效,但这仅适用于do符号内部而非显式>>=,原因是出现了额外的贬低:

instance FromJSON Link where
    parseJSON (Object o) = do
        Just link' <- o .: "link"
        tags'      <- o .: "tags"
        return $ Link link' tags'
    parseJSON _ = mzero

> -- Note that I used link :: String for my testing instead
> decode "{\"link\": \"test\", \"tags\": []}" :: Maybe Link
Just (Link {link = "test", tags=[]})
> decode "{\"tags\": []}" :: Maybe Link
Nothing

这里发生的事情是,<-左侧的失败模式匹配正在调用fail。查看Parser的{​​{3}}告诉我fail正在呼叫failDesc,这也是mzero的实施所使用的,因此在这种情况下你很安全。一般情况下,只需调用fail,根据monad可以执行任意数量的操作,但Parser我认为这是有道理的。

然而,@ shang的回答肯定更好,因为它不依赖隐式行为。