2015-03-19 5 views
1

Я не уверен, что я лаяю неправильное дерево здесь, но у меня есть определение Aeson FromJSON, которое выглядит довольно громоздким, и мне было интересно, можно ли его превратить в нечто более кратким. Я хочу закоротить синтаксический разбор всего объекта, если вложенный анализ URI завершился с ошибкой.Flatten MonadPlus внутри Aeson Parser

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 в конце?

ответ

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 
+0

О, я был так близок к одной из моих попыток сделать это. Спасибо, это именно то решение, которое я искал! – passy

1

шаблон соответствия работает, но это работает только внутри do обозначения не явной >>= из-за дополнительного desugaring, который идет на:

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 

Что здесь происходит заключается в том, что неудачное совпадение шаблона в левой части <- является вызовом ing fail. Глядя на source на номер Parser, мне сообщается, что fail звонит failDesc, который также используется при реализации mzero, поэтому в этом случае вы в безопасности. В общем, он просто вызывает fail, который может делать любое количество вещей в зависимости от монады, но для Parser Я бы сказал, что это имеет смысл.

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