Haskell - Arithmetic Possibly

I was asked to implement a function that uses the following profile:

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

and answers as follows:

 > 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 wrote the following, but it will not compile. Do you have any suggestions?

 maybe_divide :: Maybe Integer -> Maybe Integer -> Maybe Integer maybe_divide xy = case x of Just x' -> case y of Just y' | y' == 0 -> Nothing | otherwise -> x' `div` y' Nothing -> Nothing 
+4
source share
4 answers

Having separated from the type of output, I would like to point out several alternative styles that you might prefer for writing such code:

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

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

There are several ways to write this using security devices or even with the Control.Monad.guard function.

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

The second approach will start with the function:

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

Now that you have safeDiv , you can transfer it to Maybe-wrapped arguments. It is pretty close to Applicative style code, with the exception of the optional Maybe layer in the output. Fortunately, nested monads (e.g. Maybe (Maybe t) ) are trivial to crash:

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

or even

 maybe_divide = (join .) . liftM2 safeDiv 

if you are free to speak fluent.

Personally, I would use one of two variants of the tuple. But quite often it has a function like safeDiv , in which case the second form may be useful.

+12
source

You should send an error message, but

 x' `div` y' 

is of type Integer , not Maybe Integer . You might need to wrap this in Just .

+7
source

You need to wrap the successful result in Just here:

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

I think a good way to do this is to use a Monad instance for Maybe :

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

This eliminates most of the tedious checks for Nothing s and bursts with the main task of checking the division by zero.

In the Maybe monad, fail "error message" actually leads to Nothing ; the error message is discarded, so I didn't need to specify "Division by zero" , "" would be just as good.

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


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

 import Control.Applicative -- at the top of your code maybe_multiply xy = (*) <$> x <*> y 

which I find aesthetically pleasing.

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

 do a <- x b <- y c <- z return $ fabc 
+4
source

All Articles