在 Haskell 的 monad 表达式中何时使用或不使用 'return'

When to use or not to use 'return' in Haskell's monad expression

我有一个函数 a,它 1) 计算 (argument+) argument,以及 2) 将它加倍 ()。

a :: Int -> Int 
a = (id >>= (\n -> (n+))) >>= (\d -> return (d + d))

在这个函数中,我应该使用return来防止错误。

但是在这个三重函数中,returns ((argument+) argument)+ argument) 将输入参数变成三倍。我无法使用 return.

triplex :: Int -> Int 
triplex = (id >>= (\n -> (n+))) >>= (\d -> (d+))

从这些例子中,我猜 return 的简单规则是 1) 当我们使用 return 值时我们使用 return,2) 我们不使用 return 当我们 return 部分函数时。但我不确定这是正确的理解。那么,Haskell中的return背后是否有规则?

Haskell中的return函数与命令式编程语言中的return关键字关系不大——它只是一个具有普通类型签名的普通函数:

return :: Monad m => a -> m a

基本上,return 接受任何旧值并将其“提升”到 monad 中。当您将 m 替换为具体类型时,此函数的作用会更清楚一些,例如 Maybe:

return :: a -> Maybe a

上面的函数只有一个实现,那就是Just,所以return = Just对于Maybe monad。

在您的例子中,您使用的是函数 monad,(->) r,通常也称为“reader”monad。执行与 Maybe 相同的替换,我们得到以下签名:

return :: a -> r -> a

这个函数只有一个实现,即忽略它的第二个参数和return它的第一个。这就是 const 所做的,所以 return = const 用于函数。


“什么时候使用return”的问题是有道理的,但是理解了上面的内容后应该更有意义:当值[=99=时需要使用return ]ed 从传递给 >>= 的函数不是单子的,因此需要“提升”。例如,以下将是类型错误:

Just 3 >>= \x -> x + 1

具体来说,右侧需要return一个Maybe Int,但它只是return一个Int。因此,我们可以使用 return 来生成正确类型的值:

Just 3 >>= \x -> return (x + 1)

但是,请考虑类似的表达式。在以下情况下,使用 return 会出现类型错误:

Just [("a", 1), ("b", 2)] >>= \l -> lookup "c"

那是因为lookup函数的结果是已经一个Maybe Int值,所以使用return会产生一个Maybe (Maybe Int),这是错误的。

回到你使用函数 monad 的例子,(n+) 已经是一个 Int -> Int 类型的函数,所以使用 return 是不正确的(它会产生一个 Int -> Int 类型的函数=42=]).但是,d + d 只是一个 Int 类型的值,因此您需要 return 将值提升到 monad 中。


值得注意的是,在这两种情况下,您始终可以将 return 替换为其底层实现。在使用 Maybe monad 时可以使用 Just 而不是 return,在使用函数 monad 时可以使用 const 而不是 return。但是,使用 return 有两个很好的理由:

  1. 使用 return 可以让您编写可用于多种 monad 的函数。也就是说,return 让你 多态性 .

  2. 使用 return 将普通值提升为 monadic 类型是非常惯用的,因此总是看到 return 而不是看到许多不同的函数更清晰,噪音更小不同的名字。