do
on a single expression accomplishes nothing whatsoever: it's just a syntactic herald indicating that you could use action sequencing here (which would require a monad), but if you don't do that then it has just the same effect as wrapping an expression in layers of redundant parentheses.
So your example session is equivalent to this:
Prelude> 0
0
Prelude> return 0
0
Prelude> Just 0
Just 0
Prelude> return (Just 0)
Just 0
Prelude> Nothing
Nothing
Prelude> return Nothing
Nothing
Now the question is why does return
not accomplish anything here. Well, actually it does, you just don't see it because GHCi is hiding implementation details. GHCi has two fundamentally different modes of interactive evaluation:
IO
actions are executed, then the result print
ed.
- Everything else is
print
ed as it is.
Crucially, GHCi tries rather hard to interpret everything as an IO
action before proceeding with 2. So if you give it an ambiguous expression like return 0
, it notices that one possible instantiation is IO Integer
. It immediately defaults to that, executes this side-effect free action and all you see in the result is thus 0
. This only happens for IO
though, not for any other monads:
Prelude> return 0 :: IO Integer
0
Prelude> return 0 :: Maybe Integer
Just 0
Prelude> return 0 :: [] Integer
[0]
GHCi just happens to default to IO
when the monad in ambiguous, but that wouldn't happen otherwise in Haskell as such.