在 Aeson 解析器中展平 MonadPlus
Flatten MonadPlus inside an Aeson Parser
我不确定我是否在这里树错了树,但我有一个 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语句?
Applicative 解析器通常更简洁,您可以使用 maybe mzero return
将 Nothing
转换为 mzero
来组合 parseURI
的结果。
instance FromJSON Link where
parseJSON :: Value -> Parser Link
parseJSON (Object o) = Link
<$> (maybe mzero return . parseURI =<< o .: "link")
<*> o .: "tags"
parseJSON _ = mzero
模式匹配有效,但这仅在 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
的 source 告诉我 fail
正在调用 failDesc
,它也被 mzero
的实现使用,所以在这种情况下你安全了。一般来说,它只是调用 fail
,它可以根据 monad 做任意数量的事情,但是对于 Parser
我认为这是有道理的。
但是,@shang 的答案肯定更好,因为它不依赖于隐式行为。
我不确定我是否在这里树错了树,但我有一个 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语句?
Applicative 解析器通常更简洁,您可以使用 maybe mzero return
将 Nothing
转换为 mzero
来组合 parseURI
的结果。
instance FromJSON Link where
parseJSON :: Value -> Parser Link
parseJSON (Object o) = Link
<$> (maybe mzero return . parseURI =<< o .: "link")
<*> o .: "tags"
parseJSON _ = mzero
模式匹配有效,但这仅在 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
的 source 告诉我 fail
正在调用 failDesc
,它也被 mzero
的实现使用,所以在这种情况下你安全了。一般来说,它只是调用 fail
,它可以根据 monad 做任意数量的事情,但是对于 Parser
我认为这是有道理的。
但是,@shang 的答案肯定更好,因为它不依赖于隐式行为。