Home > Enterprise >  Pass more than 1 parameter to monad
Pass more than 1 parameter to monad

Time:05-30

I'm learning Haskell and making up some examples. I'm not sure why the second example doesn't work

foo :: Int -> Int -> Maybe Int
foo 0 0 = Nothing
foo a b = Just $ a   b

bar :: Int -> Maybe Int
bar 0 = Nothing
bar a = Just $ a   1

-- This works
Just 4 >>= bar

-- Why this doesnt work?
(Just 4 Just 4) >>= foo

-- This works
do
    a <- Just 3
    b <- Just 4
    foo a b

Thank you!

CodePudding user response:

As the comment says, (Just 4 Just 4) tries to apply the constructor Just to 3 arguments when it only takes one. So, I will assume that you wanted something like (Just 4, Just 4), and want it to work like your final example.

The type of the "bind" operator is (>>=) :: Monad m => m a -> (m a -> b) -> m b. This means that the function expected after the operator only takes one argument, not two. So, again, the ultimate reason why it doesn't work is that, your function takes the wrong number of arguments. (Partial application means that you don't have to provide all the arguments at once, but it sounds like you're expecting some other piece of data to be magically routed to the missing argument...)

Desugaring your do example to >>= form translates as:

Just 3 >>= \a -> Just 4 >>= \b -> foo a b

To make this a little clearer, I'll parenthesize the lambdas:

Just 3 >>= ( \a -> Just 4 >>= (\b -> foo a b) )

That makes it easier to see that you can simplify the inner lambda:

Just 3 >>= ( \a -> Just 4 >>= foo a )

So, it's possible after all to route the missing data to the extra argument! But, you do have to work out the routing yourself...

There's nothing particularly magical about Haskell functions; they tend to be more particular about how they're called than dynamic languages. The largest "magic" here is that the type checker can often tell when you're not using them correctly.

And (as the other answer notes) there is nothing magical about >>= -- it's just another function, and in order to understand how to use it, you need to take a look at its type.

CodePudding user response:

It doesn't work because >>= is a perfectly normal operator (and operators are perfectly normal functions).

You seem to be thinking of >>= as special syntax for getting values out of the monadic value on its left and feeding it to the function on the right. It is not special syntax; rather >>= itself is a function that gets applied to the values on its left and its right (and then computes a result as you expect).

However, that means that the left and right arguments must be valid expressions for things that could exist as ordinary values; things you could simply bind to variables with var = <expr> syntax. Just 4 >>= bar works because (among other requirements) Just 4 on its own is a valid expression of type Maybe Int and bar is a valid expression of type Int -> Maybe Int. Just 4 Just 4 >>= foo doesn't work because Just 4 Just 4 is not a correct expression (what would it's type be?); it's interpreted as applying Just to the 3 separate arguments 4, Just, and 4, whereas you want it to be two separate values Just 4 and Just 4. But even if you could get the compiler to interpret something there as two separate values, there's no way for >>= to be passed two separate values as its left argument; it's expecting (in this usage) a single value of type Just Int.

If you have a function like foo that needs two arguments and you want to source those arguments from values that are in a monadic context, then you can't just apply >>= you need to write code that does that (like your final example with the do block; there are many other ways to do something equivalent).

  • Related