可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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
回答1:
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.
回答2:
I suspect that you're misunderstanding what "return" means in the context of a monad in Haskell. return is a function that takes in an a
and returns a "wrapped a
" -- that is, the simplest possible instance of the monad. In other languages it is often called Unit
. It's not the "control flow" return
that you see in C-like languages.
So in your example of the Maybe
monad, we have return defined as a function that takes in an a
and returns a Maybe a
:
return :: a -> Maybe a
And what does it do? if you give it x
, it gives you back Just x
:
return x = Just x
And now you can use return
as a shorthand when you need that function, rather than writing out:
\x -> Just x
It's called return
because when you're writing out monads in do
notation, it looks like what you'd do in a C-like language.
回答3:
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.