Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's so special about 'return' keyword

When I seemed to understand what return is for in Haskell, I tried to play with different alternatives and it seems that return not only can be used anywhere in the monad chain, but also can be excluded completely

*Main> Just 9 >>= \y -> (Just y) >>= \x -> return x
Just 9

*Main> Just 9 >>= \y -> (return y) >>= \x -> (Just y)
Just 9

*Main> Just 9 >>= \y -> (Just y) >>= \x -> (Just x)
Just 9 

Even if I omit return in my own instancing, I only get warning...

data MaybeG a = NothingG | JustG a deriving Show 
instance Monad MaybeG where  
    --    return x = JustG x  
        NothingG >>= f = NothingG  
        JustG x >>= f  = f x  
        fail _ = NothingG  

Monad.hs:3:10:
    Warning: No explicit method nor default method for `return'
    In the instance declaration for `Monad MaybeG'

and I still can use the monad

*Main> JustG 9 >>= \y -> (JustG 11) >>= \x -> (JustG y)
JustG 9

*Main> JustG 9 >>= \y -> (NothingG) >>= \x -> (JustG y)
NothingG

So what's so special about the return keyword? Is this about more complex cases where I can not omit it? Or because this is the "right" way to do things even if they can be done differently?

UPDATE: .. or another alternative, I could define my own monadic value constructor

finallyMyLastStepG :: Int -> MaybeG Int
finallyMyLastStepG a = JustG a  

and produce another variant of the same chain (with the same result)

*Main> JustG 9 >>= \y -> (JustG 11) >>= \x -> (finallyMyLastStepG y)
JustG 9
like image 886
Maksee Avatar asked Mar 10 '13 15:03

Maksee


People also ask

What is the significance of return keyword?

A return statement ends the execution of a function, and returns control to the calling function. Execution resumes in the calling function at the point immediately following the call. A return statement can return a value to the calling function. For more information, see Return type.

What does the return do?

Overview. A return statement causes execution to leave the current function and resume at the point in the code immediately after where the function was called. Return statements in many languages allow a function to specify a return value to be passed back to the code that called the function.

What is the point of return Python?

The Python return statement is a key component of functions and methods. You can use the return statement to make your functions send Python objects back to the caller code. These objects are known as the function's return value. You can use them to perform further computation in your programs.

What is return keyword in Java?

In Java, the return keyword returns a value from a method. The method will return the value immediately when the keyword is encountered. This means that the method will not execute any more statements beyond the return keyword, and any local variables created in the method will be discarded.


2 Answers

So what's so special about the return keyword?

Firstly, return is not a keyword in Haskell. It is an overloaded function.

Its type is given by:

class  Monad m  where
    -- | Sequentially compose two actions, passing any value produced
    -- by the first as an argument to the second.
    (>>=)       :: m a -> (a -> m b) -> m b

    -- | Inject a value into the monadic type.
    return      :: a -> m a

So you see that return is a function that given a value of type a, returns a new value of type m a, where m is some type that is an instance of Monad. Such types include:

  • Monad []
  • Monad I0
  • Monad Maybe
  • Monad STM
  • Monad ((->) r)
  • Monad (Either e)
  • Monad (ST s)

and many more besides. Instances of 'Monad' should satisfy the following laws:

> return a >>= k  ==  k a
> m >>= return  ==  m
> m >>= (\x -> k x >>= h)  ==  (m >>= k) >>= h

The implementation of a function a -> m a is pretty easy to guess. Here's the definition for the most common monads:

Lists:

 return x = [x]

Maybe

 return x = Just x

So you see that the return is an overloaded function that "lifts" a value into a monadic wrapper. You can thus use it anywhere you can use its definition. E.g.

Prelude> 1 : return 2
[1,2]

or in the do notion (useful when chaining expressions).

> do v <- return 7 ; return v :: Maybe Int
Just 7

The real reason to use a monadic return is when composing multiple values in some monad:

Prelude> do x <- return 1 ; y <- return 2 ; return (x + y) :: Maybe Int
Just 3
Prelude> do x <- Nothing  ; y <- return 2 ; return y
Nothing

In the last statement you see how the chain short-circuited once it hit a zero value for the given monad. In this case Nothing.

Summary: return is an overloaded function that lifts a value into a monadic wrapper. You use it when you need to lift values. It is not a control-flow keyword, as it is in imperative languages.

like image 100
Don Stewart Avatar answered Oct 31 '22 21:10

Don Stewart


Mike Hartl comment led me to the right direction, although was not so formal imho, so I just post my final understanding what so special about 'return' operator.

Any type class lists operator it supports and there are functions that can work only in this class context (imposed via class constratint symbol =>). So, for example filterM signature

filterM :: Monad m => (a -> m Bool) -> [a] -> m [a] 

shows us that it can be used only in monadic context. The magic is that in the body this function is free to use any operator the class has (>>= and return for Monad) and if an instance (for example my MaybeG ) lacks a method (return in my case) then the function can fail. So when the return is there

> filterM (\x -> JustG (x > 0)) [2, 1, 0, -1] 
JustG [2,1]

and when it's commented (see my implementation of MaybeG in the question)

> filterM (\x -> JustG (x > 0)) [2, 1, 0, -1] 

*** Exception: Monad.hs:3:10-21: No instance nor default method for class operation GHC.Base.return

so imlementation of any operator (return in monad case) is required if one plans to use the instance with functions working with this class (monad in this case) constraint.

I think my initial misunderstanding was due to the fact that most tutorials explains monadic chains without polymorphic (ad hoc) context. This context in my opinion makes monads more powerful and reusable.

like image 30
Maksee Avatar answered Oct 31 '22 21:10

Maksee