這裏是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