1

I have a function a that 1) computes (argument+) argument, and 2) doubles it (Interpreting Haskell monad example).

a :: Int -> Int 
a = (id >>= (\n -> (n+))) >>= (\d -> return (d + d))

In this function, I should use return to prevent errors.

However in this triple function that returns ((argument+) argument)+ argument) to triple the input argument. I cannot use the return.

triplex :: Int -> Int 
triplex = (id >>= (\n -> (n+))) >>= (\d -> (d+))

From these examples, I guess simply the rule of return is 1) we use return when we return values, 2) we don't use return when we return a partial function. But I'm not sure this is correct understanding. So, Is there a rule behind this return in Haskell?

Community
  • 1
  • 1
prosseek
  • 182,215
  • 215
  • 566
  • 871
  • 3
    well yes - `return` is used to pull values up into your monad - as every other function you just have to look at the types (`return` is not what you know from C-like languages) - aside from this: why use the monad-instance of `(->) a` here at all? it just obfuscates everything - is this some exercise? – Random Dev Jul 28 '16 at 04:18
  • 1
    see: `\n -> (n+)` already has the desired type `Int -> (Int -> Int)` (your monad is `(->) Int`) - but of course `d+d` has not (it has type `Int`) - so you use `return (d+d) = \ _ -> (d+d)` (remember `the `\d -> ...` in `>>= (\d -> ...)` needs to return a monadic value - that is a `Int -> a` here ) – Random Dev Jul 28 '16 at 04:20
  • @Carsten: Nice answer, I think you can answer the question for me accept the question unless better answers come along. BTW, I'm new to Haskell, so I'm trying to understand what monad in Haskell is. – prosseek Jul 28 '16 at 04:39
  • 1
    This question does not deserve the down-vote, imho. Because it is an honest question by a beginner. – akonsu Jul 28 '16 at 04:42
  • @prosseek, if I may, I think your approach to figuring out how to make it work is flawed: you are looking for "rules" which would give you an "algorithm" of sort, whereas I think the right approach is simply to understand the types of each part of the expression and how it works. – akonsu Jul 28 '16 at 04:48
  • 1
    @prosseek You have asked a bunch of questions which are tailored to only one specific monad, the `(->) a` monad, which is not that much used. To get what's a monad is about I'd recommend you also play with other monads. Start from e.g. `Maybe` and `[]` and `Either e`, and then move to `State s`, `IO`, etc. Just focusing on one monad will not make you understand the whole picture. – chi Jul 28 '16 at 07:30

1 Answers1

9

The return function in Haskell has little to do with the return keyword in imperative programming languages—it’s just an ordinary function with an ordinary type signature:

return :: Monad m => a -> m a

Basically, return takes any old value and “lifts” it into a monad. It’s a little clearer what this function does when you replace the m with a concrete type, like Maybe:

return :: a -> Maybe a

There’s only one implementation of the above function, and that’s Just, so return = Just for the Maybe monad.

In your case, you are using the function monad, (->) r, also often called the “reader” monad. Performing the same substitution as with Maybe, we get the following signature:

return :: a -> r -> a

This function also has only one implementation, which is to ignore its second argument and return its first. This is what const does, so return = const for functions.


The question of “when to use return” is a reasonable one, but it should make more sense after understanding the above: you need to use return when the value returned from a function passed to >>= is not monadic, so it needs to be “lifted”. For example, the following would be a type error:

Just 3 >>= \x -> x + 1

Specifically, the right hand side needs to return a Maybe Int, but it only returns an Int. Therefore, we can use return to produce a value of the correct type:

Just 3 >>= \x -> return (x + 1)

However, consider a similar expression. In the following case, using return would be a type error:

Just [("a", 1), ("b", 2)] >>= \l -> lookup "c"

That’s because the result of the lookup function is already a Maybe Int value, so using return would produce a Maybe (Maybe Int), which would be wrong.

Returning to your example using the function monad, (n+) is already a function of type Int -> Int, so using return would be incorrect (it would produce a function of type Int -> Int -> Int). However, d + d is just a value of type Int, so you need return to lift the value into the monad.


It’s worth noting that, in both cases, you could always replace return with its underlying implementation. You could use Just instead of return when using the Maybe monad, and you could use const instead of return when using the function monad. However, there are two good reasons to use return:

  1. Using return lets you write functions that work with more than one kind of monad. That is, return gets you polymorphism.

  2. It’s extremely idiomatic to use return to lift plain values into a monadic type, so it’s clearer and less noisy to always see return instead of seeing many different functions with different names.

Alexis King
  • 43,109
  • 15
  • 131
  • 205