我不确定我是否在这里咆哮错误的树,但我有一个看起来相当笨重的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 URI
,Maybe
和Parser
都有MonadPlus
个实例。有没有办法直接组合这两个并在最后删除丑陋的case语句?
答案 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的回答肯定更好,因为它不依赖隐式行为。