Reputation: 183
I am working with Haskell and maybe monads but I am a little bit confused with them here is my code but I am getting error and I do not know how to improve my code.
doAdd :: Int -> Int -> Maybe Int
doAdd x y = do
result <- x + y
return result
Upvotes: 1
Views: 164
Reputation: 30227
Let's look critically at the type of the function that you're writing:
doAdd :: Int -> Int -> Maybe Int
The point of the Maybe
monad is to work with types that are wrapped with a Maybe
type constructor. In your case, the two Int
arguments are just plain Int
s, and the +
function always produces an Int
so there is no need for the monad.
If instead, your function took Maybe Int
as its arguments, then you could use do
notation to handle the Nothing
case behind the scenes:
doAdd :: Maybe Int -> Maybe Int -> Maybe Int
doAdd mx my = do x <- mx
y <- my
return (x + y)
example1 = doAdd (Just 1) (Just 3) -- => Just 4
example2 = doAdd (Just 1) Nothing -- => Nothing
example3 = doAdd Nothing (Just 3) -- => Nothing
example4 = doAdd Nothing Nothing -- => Nothing
But we can extract a pattern from this: what you are doing, more generically, is taking a function ((+) :: Int -> Int -> Int
) and adapting it to work in the case where the arguments it wants are "inside" a monad. We can abstract away from the specific function (+
) and the specific monad (Maybe
) and get this generic function:
liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
liftM2 f ma mb = do a <- ma
b <- mb
return (f a b)
Now with liftM2
you can write:
doAdd :: Maybe Int -> Maybe Int -> Maybe Int
doAdd = liftM2 (+)
The reason why I chose the name liftM2
is because this is actually a library function—you don't need to write it, you can import the Control.Monad
module and you'll get it for free.
What would be a better example of using the Maybe
monad? When you have an operation that, unlike +
, can intrinsically can produce a Maybe
result. One idea would be if you wanted to catch division by 0 mistakes. You could write a "safe" version of the div
function:
-- | Returns `Nothing` if second argument is zero.
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv x y = Just (x `div` y)
Now in this case the monad does become more useful:
-- | This function tests whether `x` is divisible by `y`. Returns `Nothing` if
-- division by zero.
divisibleBy :: Int -> Int -> Maybe Bool
divisibleBy x y = do z <- safeDiv x y
let x' = z * y
return (x == x')
Another more interesting monad example is if you have operations that return more than one value—for example, positive and negative square roots:
-- Compute both square roots of x.
allSqrt x = [sqrt x, -(sqrt x)]
-- Example: add the square roots of 5 to those of 7.
example = do x <- allSqrt 5
y <- allSqrt 7
return (x + y)
Or using liftM2
from above:
example = liftM2 (+) (allSqrt 5) (allSqrt 7)
So anyway, a good rule of thumb is this: never "pollute" a function with a monad type if it doesn't really need it. Your original doAdd
—and even my rewritten version—are a violation of this rule of thumb, because what the function does is adding, but adding has nothing to do with Maybe
—the Nothing
handling is just a behavior that we add on top of the core function (+)
. The reason for this rule of thumb is that any function that does not use monads can be generically adapted to add the behavior of any monad you want, using utility functions like liftM2
(and many other similar utility functions).
On the other hand, safeDiv
and allSqrt
are examples where you can't really write the function you want without using Maybe
or []
; if you are dealing with a function like that, then monads are often a convenient abstraction for eliminating boilerplate code.
Upvotes: 8
Reputation: 54058
A better example might be
justPositive :: Num a => a -> Maybe a
justPositive x
| x <= 0 = Nothing
| otherwise = Just x
addPositives x y = do
x' <- justPositive x
y' <- justPositive y
return $ x' + y'
This will filter out any non-positive values passed into the function using do notation
Upvotes: 3
Reputation: 12070
The use case you give doesn't justify do notation, but this is a more common use case- You can chain functions of this type together.
func::Int->Int->Maybe Int -- func would be a function like divide, which is undefined for division by zero
main = do
result1 <- func 1 2
result2 <- func 3 4
result3 <- func result1 result2
return result3
This is the whole point of monads anyway, chaining together functions of type a->m a.
When used this way, the Maybe monad acts much like exceptions in Java (you can use Either if you want to propagate a message up).
Upvotes: 0
Reputation: 237030
That isn't how you'd write that code. The <-
operator is for getting a value out of a monad. The result of x + y
is just a number, not a monad wrapping a number.
Do notation is actually completely wasteful here. If you were bound and determined to write it that way, it would have to look like this:
doAdd x y = do
let result = x + y
return result
But that's just a longwinded version of this:
doAdd x y = return $ x + y
Which is in turn equivalent to
doAdd x y = Just $ x + y
Which is how you'd actually write something like this.
Upvotes: 1