Question

I have been asked to implement a function which uses the following profile:

maybe_divide :: Maybe Integer -> Maybe Integer -> Maybe Integer

and responds in the following manner:

> maybe_divide (Just 5) (Just 2) 
Just 2
> maybe_divide (Just (-5)) (Just 2) 
Just (-3)
> maybe_divide (Just (-5)) (Just 0) 
Nothing
> maybe_divide Nothing (Just 1) 
Nothing
> maybe_divide (Just 1) Nothing
Nothing

I have written the following but it will not compile. Do you guys have any suggestions?

maybe_divide :: Maybe Integer -> Maybe Integer -> Maybe Integer
maybe_divide x y = case x of
    Just x' -> case y of 
        Just y'
            | y' == 0 -> Nothing  
            | otherwise -> x' `div` y'
    Nothing -> Nothing 
Was it helpful?

Solution

Separate from the output type, I'd like to point out a few alternative styles you may prefer for writing this sort of code:

First, you can case on multiple expressions by putting them in a tuple:

case (x,y) of
    (_      , Just 0) -> Nothing
    (Just x', Just y') -> Just (x' `div` y')
    _ -> Nothing

there are a couple different ways to write this using guards, or even the function Control.Monad.guard.

case (x,y) of
  (Just x', Just y') -> (x' `div` y') <$ guard (y' /= 0)
  _ -> Nothing

The second approach would start with a function:

safeDiv :: Integer -> Integer -> Maybe Integer
safeDiv x 0 = Nothing
safeDiv x y = Just (x `div` y)

Now that you have safeDiv, you can lift it into Maybe-wrapped arguments. It's pretty close to Applicative style code, except for an extra layer of Maybe in the output. Fortunately nested monads (e.g. Maybe (Maybe t)) are trivial to collapse:

maybe_divide x y = join $ safeDiv <$> x <*> y

or even

maybe_divide = (join .) . liftM2 safeDiv

if you're fluent in point-free.

Personally, I would use one of the two tuple-case variants. But it's fairly common to already have a function like safeDiv, in which case the second form can be useful.

OTHER TIPS

You should post the error you are getting, but

x' `div` y'

has type Integer and not Maybe Integer. Perhaps you need to wrap this in a Just.

You need to wrap the successful result in Just here:

... | otherwise -> Just (x' `div` y')

I think a nice way of doing this is to use the Monad instance for Maybe:

maybe_divide x y = do
   a <- x
   b <- y
   if b == 0 
     then fail "Division by zero" 
     else return (a/b)

This gets rid of most of the tedious checking for Nothings, and cracks on with the main business of checking for divide by zero.

In the Maybe monad, fail "error message" actually results in a Nothing; the error message is discarded, so I needn't have said "Division by zero", "" would have done just as well.

In the Maybe monad, return means "wrap in a Just".


If you don't need to do any error checking, you can use the applicative instance:

import Control.Applicative -- at the top of your code

maybe_multiply x y = (*) <$> x <*> y

which I think is aethetically pleasing.

This is because for an Applicative instance that matches a Monad, f <$> x <*> y <*> z is equivalent to

do 
  a <- x
  b <- y
  c <- z
  return $ f a b c
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top