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

时间:2016-10-19 12:35:02

标签: haskell functional-programming monads applicative

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

以下是Javascript中fmap<*>>>=类型类的FunctorApplicativeMonad的实现:

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中急切的评估顺序):

  • applyf(x) ... g(x) ... lambda(result of g) ... lambda的结果
  • bindf(x) ... g(result of f) ... lambda(x) ... lambda的结果

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

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

2 个答案:

答案 0 :(得分:5)

Lee's answer的几个脚注:

  

然而,bind函数看起来很奇怪。为什么fg   反过来嵌套?

因为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 ),从理论的角度来看(=<<)是最自然的写作方式。特别是,与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
  

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

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

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

Monad超越了Applicative,提供了根据之前的结果确定下一个效果的方法(而不是“之前的效果” - Applicative可以做到这一点)。在这种情况下,效果由一个函数组成,该函数在给定类型r的参数的情况下生成值。现在,由于可以翻转具有多个参数的函数(即返回函数的函数),因此(r -> (a -> b))(a -> (r -> b))之间没有显着差异(flip可以将一个变为一个其他),这使得Monad的{​​{1}}实例完全等同于(->) r实例。

答案 1 :(得分:4)

对于某些固定类型r -> a,函数的monad实例中的值具有类型r。赋给(a -> (r -> b))的函数(>>=)允许您根据当前值(函数r -> a)选择要返回的下一个函数。 f r的类型为ak (f r)的类型为r -> 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; }
});

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

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