当我似乎理解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
答案 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
的一个实例。这些类型包括:
[]
I0
Maybe
STM
((->) r)
(Either e)
(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
notion(chaining 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)
它支持的任何类型类列表运算符,并且有些函数只能在此类上下文中起作用(通过类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更强大和可重用。