如何解释函数实例的 bind/>>=?

How to interpret bind/>>= of the function instance?

我试图通过在 Javascript 中实现它们的函数实例来提高我对 ApplicativeMonad 的理解。我对 Haskell 的了解有限,我希望我的问题完全有意义。

这是我对 FunctorApplicativeMonad 类型类的 fmap<*>>>= 的实现64=]:

const fmap = f => g => x => f(g(x)); // B combinator
const apply = f => g => x => f(x) (g(x)); // S combinator
const bind = f => g => x => g(f(x)) (x); // ?

我不确定 bind 是否是 Haskell 实现的正确翻译:

(>>=)  :: (r -> a) -> (a -> (r -> b)) -> r -> b

instance Monad ((->) r) where
f >>= k = \ r -> k (f r) r

假设bind是正确的,它是如何解释的?我知道 Applicative 可以对有效的计算进行排序。我也知道还有一个Monad可以让你根据前一个效果来决定下一个效果

我可以看到序列(Javascript 中的求值顺序):

但是,bind 函数看起来很奇怪。为什么 fg 以相反的方式嵌套?具体的Monad行为(根据前一个效果决定下一个效果)在这个实现中是如何体现的?实际上 g(f(x)) (x) 看起来像一个带有翻转参数的函数组合,其中 g 是一个二元函数。

当我对一元和二元函数应用 apply/bind 时,它们产生相同的结果。这没有多大意义。

函数的 monad 实例中的值具有某些固定类型 r 的类型 r -> a。给定 (>>=) 的函数 (a -> (r -> b)) 允许您根据当前值(函数 r -> a )的结果选择下一个函数 return 。 f r 的类型为 ak (f r) 的类型为 r -> b,这是下一个要应用的函数。

因此,在您的代码中,g(f(x)) 是一个需要 r 类型的单个参数的函数。 bind 的调用者可以根据前一个函数的值 return 选择此函数,例如

var inc = x => x + 1;
var f = bind(inc)(function(i) {
   if(i <= 5) { return x => x * 2; }
   else { return x => x * 3; }
});

函数将x作为输入,并可以根据inc(x)的结果选择下一阶段的计算,例如

f(2) //4;
f(5) //15;

的一些脚注:

However, the bind function looks pretty weird. Why are f and g nested the other way around?

因为bind是倒着的。比较 (>>=) 及其翻转版本 (=<<)

(>>=) :: Monad m => m a -> (a -> m b) -> m b
(=<<) :: Monad m => (a -> m b) -> m a -> m b

或者,在您的具体示例中:

(>>=) :: (r -> a) -> (a -> (r -> b)) -> (r -> b)
(=<<) :: (a -> (r -> b)) -> (r -> a) -> (r -> b)

虽然在实践中我们更倾向于使用 (>>=) 而不是 (=<<)(因为 (>>=),从句法上讲,非常适合经常使用的流水线 monad to build),从理论上讲(=<<)是最自然的写法吧。特别是fmap/(<$>)(<*>)的相同点和不同点更明显:

(<$>) :: Functor f     =>   (a -> b) -> f a -> f b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
(=<<) :: Monad f       => (a -> f b) -> f a -> f b

When I apply apply/bind with an unary and a binary function, they yield the same result. This doesn't make much sense.

这是关于函数实例的偶然事实。让我们将专用签名并排放置:

(<*>) :: (r -> (a -> b)) -> (r -> a) -> (r -> b)
(=<<) :: (a -> (r -> b)) -> (r -> a) -> (r -> b)

Monad 超越了 Applicative,提供了根据先前的 结果 确定下一个效果的方法(与 "previous effect" 相反 - - Applicative 已经可以做到了)。在这种情况下,效果由一个函数组成,该函数在给定类型 r 的参数的情况下生成值。现在,由于可以翻转具有多个参数的函数(即 return 函数的函数),所以 (r -> (a -> b))(a -> (r -> b)) 之间没有显着差异(flip 可以简单地将一个变成另一个),这使得 (->) rMonad 实例完全等同于 Applicative 实例。