Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Use MonadPlus in (.:?) to produce whatever empty value is needed instead of always producing a Nothing on failure. This way (.:?) can be re-used for lists, Maybe, or whatever other MonadPlus you desire. #59

wants to merge 1 commit into


None yet
5 participants

mike-burns commented Dec 29, 2011

I've used this a few times in my libraries to produce lists or Maybes, as needed.

Here's an example (I had named it (.:<) in my code so as not to conflict with Aeson's, but have since changed that locally): https://github.com/mike-burns/github/blob/master/Github/Data.hs#L36

@mike-burns mike-burns Use MonadPlus in (.:?) to produce whatever empty value is needed inst…
…ead of always producing a Nothing on failure. This way (.:?) can be re-used for lists, Maybe, or whatever other MonadPlus you desire.

basvandijk commented Jan 15, 2012

I remember that the containers- release changed the lookup function from:

lookup :: (Monad m, Ord k) => k -> Map k a -> m a


lookup :: Ord k => k -> Map k a -> Maybe a

I don't remember the rationale for that change but we may want to look it up before we make the reverse change in aeson. Maybe it had something to do with type ambiguity...


basvandijk commented Jan 15, 2012

Here's the start of the relevant thread on this change and here's its conclusion.


mike-burns commented Jan 15, 2012

Interesting. I do happen to love programming history.

Seems they made the change to avoid monads where fail causes a runtime error. The only way that would happen in this function is if Aeson were asked to parse a JSON value into an IO value. While that's maybe possible, I doubt someone would use (.:?) for that purpose.

I'd also be fine with something besides MonadPlus. Monoid's mempty or Alternative's empty would also satisfy the desired goal.


hvr commented Mar 5, 2012

Btw & jfyi, I've been using a more pragmatic approach since the earliest aeson versions in my code:

I have a type-class JsonDefault (inspired by the more generic Default package that should be on hackage somewhere) which provides default values for types deserializable from JSON:

class JsonDefault a where
    jdefault :: a

-- |Helper for extracting type-converted field from 'JSObject' with
-- default value fallback infered via 'JsonDefault' instance.
(.:!) :: (JsonDefault a, FromJSON a) => Object -> Text -> Parser a
obj .:! key = fromMaybe jdefault <$> obj .:? key
{-# INLINE (.:!) #-}

Besides encoding default application-specific values, I also use this to encode JSON-encoding conventions specific to our project (e.g. such that empty lists may be omitted in JSON, or bool-typed values need only be set if they result in true), e.g.:

instance JsonDefault Bool where
  jdefault = False

instance JsonDefault [a] where
  jdefault = []

tibbe commented Jun 16, 2012

The reason Data.Map.lookup was changed is that Maybe is general enough. Once you get a Maybe back you could always do:

case lookup k m of
    Nothing -> fail
    Just v  -> return v

I don't think MonadPlus makes sense here as there's no interesting bind going on. If it is to be changed it ought to be either Monoid or Alternative, I can't see which at this point.


bos commented Jun 16, 2012

I don't like @mike-burns's original pull request, sorry.

First, Maybe is the type that encodes the least amount of information possible in this situation. It's always convertible to whatever your desired type is via a simple function, e.g. maybeToList. By making the result type more general, you force every potential user of the function to wade through the world of possible result types before they can figure out how to use it.

Secondly, even if I thought it was a good idea, MonadPlus is the wrong class to be using - there's no need for a Monad constraint here. The class with least information would be Monoid, but even it drags in an irrelevant mappend method. Maybe is the right type here.

@bos bos closed this Jun 16, 2012

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment