这里是JSON对象的一部分,它表示用户:撰写可选埃宋解析器
{ "image": { "url": "http://example.com" } }
我需要将其解析为User
类型:
data User = User { imgUrl :: Maybe Text }
天真溶液:
parseJSON (Object o) = User <$> getImgUrl o
where getImgUrl o = (o .:? "image") >>= maybe (return Nothing) (.:? "url")
但是这并不比这些链条好:
case f m1 of
Nothing -> Nothing
Just m2 -> case f2 m2 of
Nothing -> Nothing
Just m3 -> case f3 m3 ....
,常常在展示“为什么你需要一个单子”解释
因此,我需要编写解析器看起来像(.:? "url") :: Parser (Maybe a)
我试图描述与comp
功能成分:
getImgUrl :: Object -> Parser (Maybe Text)
getImgUrl o = o .:? "image" >>= comp (o .:? "url")
comp :: (Monad m) => (a -> m (Maybe b)) -> Maybe a -> m (Maybe b)
comp p Nothing = return Nothing
comp p (Just o) = p o
闻起来像一个函子,但fmap
没有帮助我。
然后我决定,即组成必须继续下去:
getImgUrl :: Object -> Parser (Maybe Text)
getImgUrl = comp2 (.:? "image") (.:? "url") o
-- Maybe should be changed to a matching typeclass
comp2 :: (Monad m) => (a -> m (Maybe b)) -> (b -> m (Maybe c)) -> a -> m (Maybe c)
comp2 = undefined
Hoogle搜索并没有帮助我,而是通过Control.Monad
文档撇给了我Kliesli组成,这我不与经验。我看到一些相似性:
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
comp2 :: Monad m => (a -> m (f b)) -> (b -> m (f c)) -> a -> m (f c)
不同的是,该组合物Maybe
期间应该“解开”。
看来我接近解决方案,但仍无法找到它。请给我一些见解。
[更新]: 我已经决定,到实际问题的最佳解决方案,将是保持原有的JSON结构,并有一个嵌套的用户类型:
data User = User { image :: Maybe Image }
data Image = Image { url :: Text }
这完全消除我的问题,并使API与原始源更兼容。
但是,仅仅出于理论目的,很高兴看到如何解决原始问题。
这似乎是对于实际问题的合理解决方案。但是,看到一些令人兴奋的通用组合器会更加出色。如果没有人会在合理的时间内提出这样的建议,我会标记你的答案。 – zudov 2015-02-06 16:14:26
另外,请参阅上面的我的更新。关于我采取的更正确的解决方案。 – zudov 2015-02-06 16:15:24
@zudov显然使用嵌套的数据结构消除了这个问题,但我肯定有实例,这是不实际的。对于需要遍历可能存在或不存在的树的情况,像上面那样的运算符是非常有用的。在镜头库中可能有一些combinator for aeson,在这里也可以使用。 – bheklilr 2015-02-06 16:19:12