“return”关键字有什么特别之处

时间:2013-03-10 15:31:02

标签: haskell functional-programming monads

当我似乎理解Haskell的回归时,我尝试使用不同的替代方案,似乎返回不仅可以在monad链中的任何地方使用,而且可以完全排除

*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 

即使我在自己的实例中省略了返回,我也只会收到警告......

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'

我仍然可以使用monad

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

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

那么return关键字有什么特别之处?这是关于更复杂的情况,我不能省略它吗?或者因为即使可以采取不同的方式,这是做事的“正确”方式吗?

更新 ..或另一种选择,我可以定义我自己的monadic值构造函数

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

并生成同一链的另一个变体(具有相同的结果)

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

3 个答案:

答案 0 :(得分:32)

  

那么关于return关键字的特别之处是什么?

首先,return 不是 Haskell中的关键字。这是一个重载的功能。

其类型由:

给出
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

因此,您看到return是一个赋值a的值的函数,返回类型m a的新值,其中m是某种类型, Monad的一个实例。这些类型包括:

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

还有更多。 Monad'的实例应符合以下法律:

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

函数a -> m a的实现很容易猜到。这是最常见的monad的定义:

解释

 return x = [x]

也许

 return x = Just x

所以你看到return是一个重载的功能,它可以提升"一个monadic包装器的值。因此,您可以在任何可以使用其定义的地方使用它。 E.g。

Prelude> 1 : return 2
[1,2]

the do notionchaining expressions时有用)。

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

使用monadic return的真正原因是在某些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

在最后一个声明中,您会看到链条在给定monad达到零值后如何短路。在这种情况下Nothing

总结:return是一个重载函数,它将值提升为monadic包装器。您需要提升值时使用它。 不是一个控制流关键字,因为它是命令式语言。

答案 1 :(得分:13)

我怀疑你误解了“回归”在Haskell中的monad语境中意味着什么。 return是一个函数,它接收a并返回一个“wrapped a” - 也就是monad最简单的实例。在其他语言中,它通常被称为Unit。这不是您在C语言中看到的“控制流”return

因此,在Maybe monad的示例中,我们将返回定义为一个函数,它接收a并返回Maybe a

return :: a -> Maybe a

它做了什么?如果你给它x,它会返回Just x

return x = Just x

现在,您可以在需要该功能时使用return作为速记,而不是写出来:

\x -> Just x

它被称为return,因为当你用do符号写出monad时,它看起来就像你用C语言做的那样。

答案 2 :(得分:2)

迈克哈特尔评论引导我走向了正确的方向,虽然不是那么正式的imho,所以我只是发布了我对'return'运算符的特别之处。

它支持的任何类型类列表运算符,并且有些函数只能在此类上下文中起作用(通过类constratint symbol =&gt;强加)。所以,例如filterM签名

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

告诉我们它只能在monadic上下文中使用。神奇的是,在正文中这个函数可以自由使用该类具有的任何运算符(&gt;&gt; =并返回Monad),如果一个实例(例如我的MaybeG)缺少一个方法(在我的情况下返回),那么功能可能会失败。所以当返回时

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

当它被评论时(参见我在问题中对MaybeG的实现)

> 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

如果计划使用具有使用此类(在本例中为monad)约束的函数的实例,则需要任何运算符(在monad情况下返回)的实现。

我认为我最初的误解是由于大多数教程解释了没有多态(ad hoc)上下文的monadic链。在我看来,这个背景使monad更强大和可重用。