Abdulsattar Mohammed
Abdulsattar Mohammed

Reputation: 10742

A function similar to (>>=) but which returns a different monad

The type of (>>=) is

(>>=) :: Monad m => m a -> (a -> m b) -> m b

I want a function that has the type:

(Monad m, Monad n) => m a -> (a -> n b) -> n b

This function could be used to chain different monads together.

I faced this problem when I was trying to get 3000 from the command-line arguments -p 3000:

main = getArgs >>= (\args -> (elemIndex "-p" args) >>= (\id -> warpDebug (fromIntegral.read (args !! (id+1))) Ilm))

This clearly won't compile because getArgs returns an IO [String] and elemIndex returns a Maybe Int. A function of the above type could be used to elegantly solve this problem. My question is:

Upvotes: 9

Views: 376

Answers (3)

nponeccop
nponeccop

Reputation: 13677

The answer depends on whether you need to use Maybe and IO monads together or separately.

If you need to use them together - the answer is that you need to compose IO and Maybe monads by constructing a stack of monad transformers containing IO monad and MaybeT monad transformer.

If you need them separately, then a simpler solution works:

import System.Environment
import Data.List

main = getArgs >>= (\args -> return (elemIndex "-p" args 
    >>= \y -> return $ y + 900) >>= print)

Note the return. So you have Maybe monad in the inner parentneses (between elemIndex and 900), but not IO. That is, you cannot perform IO actions before leaving Maybe monad, as I showed with print.

Upvotes: 2

Tikhon Jelvis
Tikhon Jelvis

Reputation: 68172

This function doesn't exist because it would not make sense for all monads. It is basically equivalent to a monad unpacking function Monad m => m a -> a--the only difference is that you immediately put it into another monad.

The reason this function is not defined for all monads is because it does not make sense for some of them. For example, take Maybe: the only way to unpack it would be to throw an error if you have Nothing, and runtime errors are looked down upon. A more extreme example would be IO--using a function that could "unpack" IO values would lead to weird and potentially nondeterministic behavior.

Thus, you do not have such a function in general. However, a lot of specific monads do come with such functions. A great example is runST; this is actually a safe way for dealing with state. You actually do have such functions for both Maybe and IO (fromJust and unsafePerformIO respectively), but they have the problems I outlined above and you should avoid them.

The solution to your problem, then, is to see if there exists such a function for whichever monads you're dealing with. If there does, check any potential pitfalls--does it generate runtime errors or cause weird behavior?

In your case, if you are absolutely sure that the Maybe is never Nothing, use fromJust. However, this is not generally good practice, so you should just stick to pattern matching the value out of the Maybe.

Upvotes: 6

Paolo Capriotti
Paolo Capriotti

Reputation: 4072

Such a function doesn't exist. In fact, if you take n to be the identity monad, it would allow you to construct a function m a -> a, which clearly cannot be defined for all monads.

To address the general problem of "composing" two monads, you can look into monad transformers.

However, it seems overkill to use monad transformers in your example. You can simply define a function [String] -> Maybe Args (for some custom type Args - say Int in the example) which does the command line argument processing, then pattern match on the result (or use maybe) from within the IO monad.

Upvotes: 21

Related Questions