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

7 monads haskell functional-programming applicative

我试图通过在Javascript中实现他们的函数实例来提高我对Applicatives和Monads的理解.我对Haskell的了解有限,我希望我的问题有道理.

下面是我的实现fmap,<*>以及>>=Functor,ApplicativeMonad在Javascript类型类:

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); // ?
Run Code Online (Sandbox Code Playgroud)

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

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

instance Monad ((->) r) where
f >>= k = \ r -> k (f r) r
Run Code Online (Sandbox Code Playgroud)

如果bind是正确的,它是如何解释的?我知道Applicative可以对有效的计算进行排序.我还知道,Monad此外,您还可以根据前一个效果确定下一个效果.

我可以看到序列(Javascript中急切的评估顺序):

  • apply:f(x)... g(x)... lambda(result of g)的...结果lambda
  • bind:f(x)... g(result of f)... lambda(x)的...结果lambda

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

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

dup*_*ode 5

李的答案的几个脚注:

但是,该bind功能看起来很奇怪。为什么fg 以相反的方式嵌套?

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

(>>=) :: Monad m => m a -> (a -> m b) -> m b
(=<<) :: Monad m => (a -> m b) -> m a -> m b
Run Code Online (Sandbox Code Playgroud)

或者,在您的特定示例中:

(>>=) :: (r -> a) -> (a -> (r -> b)) -> (r -> b)
(=<<) :: (a -> (r -> b)) -> (r -> a) -> (r -> b)
Run Code Online (Sandbox Code Playgroud)

尽管在实践中,我们倾向于使用(>>=)更多的东西(=<<)(因为从(>>=)句法上讲,如何使它很适合通常用来构建的管道monad的类型),但是从理论上讲,这(=<<)是最自然的编写方式。特别是,与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
Run Code Online (Sandbox Code Playgroud)

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

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

(<*>) :: (r -> (a -> b)) -> (r -> a) -> (r -> b)
(=<<) :: (a -> (r -> b)) -> (r -> a) -> (r -> b)
Run Code Online (Sandbox Code Playgroud)

Monad超越Applicative了提供根据先前结果确定下一个效果的方法(与“上一个效果”相反- Applicative已经可以做到)。在这种情况下,效果由一个函数产生,该函数根据给定的type参数生成值r。现在,由于具有多个参数的函数(即返回函数的函数)可以翻转,因此(r -> (a -> b))和之间没有明显区别(a -> (r -> b))flip可以简单地将一个改变为另一个),这使得该Monad实例(->) r完全等同于Applicative一个实例。


Lee*_*Lee 4

函数的 monad 实例中的值具有r -> a某种固定类型的类型r(a -> (r -> b))给定的函数(>>=)允许您选择下一个要返回的函数(给定当前值的结果)(函数r -> a)。f rhas typea并且k (f r)has typer -> b是下一个要应用的函数。

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

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

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

f(2) //4;
f(5) //15;
Run Code Online (Sandbox Code Playgroud)