3

I saw this question on SO and am trying to replicate it: Haskell: Reusing FromJSON instances with lenses, lens-aeson, and nested JSON

However, when I run what I think should be a complete example, I get an error.

Here is my code:

import Data.Aeson
import Data.Aeson.Lens
import Control.Lens
import Data.Aeson.Types

data Colour = Yellow | Green | Blue

instance FromJSON Colour where
  parseJSON (String s) = return $ case s of
        "blue" -> Blue
        "green" -> Green
        _ -> Yellow
  parseJSON _ = mzero

instance ToJSON Colour where
  toJSON Yellow = String "yellow"
  toJSON Blue   = String "blue"
  toJSON Green  = String "green"

parseColour :: String -> Maybe Colour
parseColour j = j ^? key "info" . key "colour" . _JSON

parseColour "{ \"info\": { \"colour\": \"yellow\" } }"

I get this:

 <interactive>:7:17: error: Variable not in scope: mzero :: Parser Colour

A lot of searches show an mzero variable used like this, successfully. I can't tell if it's something imported from a package or just an arbitrary variable name and I'm misusing the function. Either way, it's not clear to me why copying this code from the question seems to fail on this.

Elsie Meow
  • 31
  • 2
  • 2
    `mzero` comes from the `MonadPlus` type class, in `Control.Monad`. – chepner Apr 12 '19 at 13:08
  • 1
    [Hoogle](https://hoogle.haskell.org/?hoogle=mzero) will tell you what to import: any of Control.Monad, Distribution.Compat.Prelude.Internal, Control.Monad.Compat, Protolude, Protolude.Monad, Turtle, ClassyPrelude, RIO, Universum.Monad.Reexport, Rebase.Prelude, LLVM.Prelude,Relude.Monad.Reexport, Stack.Prelude, Yesod.Paginator.Prelude, Control.Monad.Free, OpenSuse.Prelude, Hledger.Web.Import, with Control.Monad being the most common and sanest choice. – Daniel Wagner Apr 12 '19 at 13:19

1 Answers1

4

mzero is part of the MonadPlus typeclass, defined in Control.Monad.

> import Control.Monad
> :info MonadPlus
class (GHC.Base.Alternative m, Monad m) =>
      MonadPlus (m :: * -> *) where
  mzero :: m a
  mplus :: m a -> m a -> m a

It serves as the identity for the mplus function, so that mplus mzero x == x and mplux x mzero == x.

The use of mzero in instances of FromJSON is mentioned in the documentation:

When writing an instance, use empty, mzero, or fail to make a conversion fail, e.g. if an Object is missing a required key, or the value is of the wrong type.

So in your quoted instance

instance FromJSON Colour where
  parseJSON (String s) = return $ case s of
        "blue" -> Blue
        "green" -> Green
        _ -> Yellow
  parseJSON _ = mzero

mzero indicates that no JSON value other than a String can be interpreted as encoding a Colour value.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • 3
    Is there anything in the help or hoogle tooling, etc., that would have helped me identify this without asking? – Elsie Meow Apr 12 '19 at 13:18