Maybe constructing data from Maybes

Before Applicatives, you still can do

parseFoo :: String -> Maybe Foo
parseFoo line = 
  case (parseBar line, parseBaz line) of
    (Just bar, Just baz) -> Just Foo { bar=bar baz=baz }
    _ -> Nothing

and avoid defining the parseFoo' function.


This is what the Monad instance of Maybe is all about. You can thus calculate your Maybe Foo with:

parseFoo :: String -> Maybe Foo
parseFoo line = do
    bar <- parseBar line
    baz <- parseBaz line
    parseFoo' bar baz

Here bar and baz are not Maybe Bar and Maybe Baz objects, but Bar and Baz objects. You thus can define a parseFoo with:

parseFoo' :: Bar -> Baz -> Maybe Foo
parseFoo' bar baz = Just Foo { bar=bar baz=baz }

Here from the moment a computation returns a Nothing, it means that the result is a Nothing, so only if the parseBar line returns a Just …, and the parseBaz returns a Just …, it will thus return the result of parseFoo bar baz.

This is because the Monad instance of Maybe is implemented as:

instance Monad Maybe where
    return = Just
    Nothing >>= _ = Nothing
    Just x >>= f = f x

A do block is syntactical sugar, so the above do block is converted to:

parseFoo line = parseBar line >>= \bar -> (parseBaz line >>= \baz -> parseFoo bar baz)

If parseFoo always returns Just in case it retrieves two Justs, we can implement this as:

parseFoo' :: Bar -> Baz -> Foo
parseFoo' bar baz = Foo { bar=bar baz=baz }

In that case we can make ues of (<$>) :: Functor f => (a -> b) -> f a -> f b and (<*>) :: Applicative f => f (a -> b) -> f a -> f b to process the data:

parseFoo :: String -> Maybe Foo
parseFoo line = parseFoo' <$> parseBar line <*> parseBaz line

In this specific case, parseFoo is however semantically the same as Foo, so we do not need a parseFoo, and can work with:

parseFoo :: String -> Maybe Foo
parseFoo line = Foo <$> parseBar line <*> parseBaz line

Luckily you don't need the full power of monads for this. Applicative functors are all you need:

parseFoo line = Foo <$> parseBar line <*> parseBaz line

Tags:

Haskell