Свести MonadPlus внутри парсера 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

Тип parseURIparseURI :: String -> Maybe URI, а Maybe и Parser имеют MonadPlus экземпляров. Есть ли способ скомпоновать два напрямую и удалить уродливый оператор case в конце?


person passy    schedule 19.03.2015    source источник


Ответы (2)


Аппликативные синтаксические анализаторы обычно более лаконичны, и вы можете составьте результат 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
person shang    schedule 19.03.2015
comment
О, я был так близок в одной из моих попыток сделать это. Спасибо, это именно то решение, которое я искал! - person passy; 20.03.2015

Сопоставление с образцом работает, но это работает только внутри нотации 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. Глядя на источник for Parser говорит мне, что fail вызывает failDesc, который также используется реализацией mzero, так что в этом случае вы в безопасности. В общем, он просто вызывает fail, который может делать любое количество вещей в зависимости от монады, но для Parser я бы сказал, что это имеет смысл.

Однако ответ @shang определенно лучше, поскольку он не полагается на неявное поведение.

person bheklilr    schedule 19.03.2015