1
votes

Haskell beginner here. I want to pass a "type" parameter with JSON, and have it parse into a native Haskell type using read. The following works:

data CreatureType = Bot | Player deriving (Generic, Typeable, Show, Read)
data Creature = Creature CreatureType deriving (Typeable, Show)

instance FromJSON Creature where
    parseJSON (A.Object v) = Creature <$> read <$> (v .: "type")  
    parseJSON _          = mzero

I want to change it to work with readMay, so that it doesn't crash if read fails. I keep getting stuck. parseJSON needs to return mzero if readMay returns Nothing.

Main Question: How can I get this function to use readMay to return mzero on a failure with read?

Here's what I've tried:

instance FromJSON Creature where
    parseJSON (A.Object v) = do
        let t = (v .: "type") :: Parser String
            mt = readMay <$> t :: Parser (Maybe CreatureType)
        -- ?? 
        -- guard -- I can't use this because it wants a Bool, and I only have Parser Bool
        -- Creature <$> read <$> mt
    parseJSON _          = mzero

mt is Parser (Maybe CreatureType). How can I change what I return if it is Nothing? I can't use pattern matching, because Data.Aeson doesn't seem to export a value constructor for Parser. I can't seem to find any methods that will give me the value inside a parser, so, is there a generic haskell method that does it? I've looked at Control.Applicative and can't find anything. Here are the docs for Data.Aeson. http://hackage.haskell.org/packages/archive/aeson/0.5.0.0/doc/html/Data-Aeson.html

1

1 Answers

1
votes

Doesn't binding the value in the do-block work?

maybeCreature <- mt
case maybeCreature of
  Nothing -> mzero
  Just cr -> return (Creature cr)

or similar?

instance FromJSON Creature where
    parseJSON (A.Object v) = do
        let t = (v .: "type") :: Parser String
            mt = readMay <$> t :: Parser (Maybe CreatureType)
        maybeCreature <- mt
        -- variant 1, guard
        guard (isJust maybeCreature)
        return (Creature $ fromJust maybeCreature)
        -- variant 2, fromMaybe
        fromMaybe mzero (return . Creature) maybeCreature
        -- variant 3, case, see above
    parseJSON _          = mzero