Do notation for monad in function returning a different type

  • A+
Category:Languages

Is there a way to write do notation for a monad in a function which the return type isn't of said monad?

I have a main function doing most of the logic of the code, supplemented by another function which does some calculations for it in the middle. The supplementary function might fail, which is why it is returning a Maybe value. I'm looking to use the do notation for the returned values in the main function. Giving a generic example:

-- does some computation to two Ints which might fail compute :: Int -> Int -> Maybe Int  -- actual logic  main :: Int -> Int -> Int main x y = do   first <- compute x y   second <- compute (x+2) (y+2)   third <- compute (x+4) (y+4)   -- does some Int calculation to first, second and third 

What I intend is for first, second, and third to have the actual Int values, taken out of the Maybe context, but doing the way above makes Haskell complain about not being able to match types of Maybe Int with Int.

Is there a way to do this? Or am I heading towards the wrong direction?

Pardon me if some terminology is wrongly used, I'm new to Haskell and still trying to wrap my head around everything.

EDIT

main has to return an Int, without being wrapped in Maybe, as there is another part of the code using the result of mainas Int. The results of a single compute might fail, but they should collectively pass (i.e. at least one would pass) in main, and what I'm looking for is a way to use do notation to take them out of Maybe, do some simple Int calculations to them (e.g. possibly treating any Nothing returned as 0), and return the final value as just Int.

 


Well the signature is in essence wrong. The result should be a Maybe Int:

main :: Int -> Int -> Maybe Int main x y = do   first <- compute x y   second <- compute (x+2) (y+2)   third <- compute (x+4) (y+4)   return (first + second + third)

For example here we return (first + second + third), and the return will wrap these in a Just data constructor.

This is because your do block, implicitly uses the >>= of the Monad Maybe, which is defined as:

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

So that means that it will indeed "unpack" values out of a Just data constructor, but in case a Nothing comes out of it, then this means that the result of the entire do block will be Nothing.

This is more or less the convenience the Monad Maybe offers: you can make computations as a chain of succesful actions, and in case one of these fails, the result will be Nothing, otherwise it will be Just result.

You can thus not at the end return an Int instead of a Maybe Int, since it is definitely possible - from the perspective of the types - that one or more computations can return a Nothing.

You can however "post" process the result of the do block, if you for example add a "default" value that will be used in case one of the computations is Nothing, like:

import Data.Maybe(fromMaybe)  main :: Int -> Int -> Int main x y = fromMaybe 0 $ do   first <- compute x y   second <- compute (x+2) (y+2)   third <- compute (x+4) (y+4)   return (first + second + third)

Here in case the do-block thus returns a Nothing, we replace it with 0 (you can of course add another value in the fromMaybe :: a -> Maybe a -> a as a value in case the computation "fails").

If you want to return the first element in a list of Maybes that is Just, then you can use asum :: (Foldable t, Alternative f) => t (f a) -> f a, so then you can write your main like:

-- first non-failing computation  import Data.Foldable(asum) import Data.Maybe(fromMaybe)  main :: Int -> Int -> Int main x y = fromMaybe 0 $ asum [     compute x y     compute (x+2) (y+2)     compute (x+4) (y+4) ]

Note that the asum can still contain only Nothings, so you still need to do some post-processing.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: