如何解释函数实例的 bind/>>=?
How to interpret bind/>>= of the function instance?
我试图通过在 Javascript 中实现它们的函数实例来提高我对 Applicative
和 Monad
的理解。我对 Haskell 的了解有限,我希望我的问题完全有意义。
这是我对 Functor
、Applicative
和 Monad
类型类的 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 中的求值顺序):
apply
: f(x)
... g(x)
... lambda(result of g)
... lambda
的结果
bind
: f(x)
... g(result of f)
... lambda(x)
... lambda
的结果
但是,bind
函数看起来很奇怪。为什么 f
和 g
以相反的方式嵌套?具体的Monad
行为(根据前一个效果决定下一个效果)在这个实现中是如何体现的?实际上 g(f(x)) (x)
看起来像一个带有翻转参数的函数组合,其中 g
是一个二元函数。
当我对一元和二元函数应用 apply
/bind
时,它们产生相同的结果。这没有多大意义。
函数的 monad 实例中的值具有某些固定类型 r
的类型 r -> a
。给定 (>>=)
的函数 (a -> (r -> b))
允许您根据当前值(函数 r -> a
)的结果选择下一个函数 return 。 f r
的类型为 a
,k (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
可以简单地将一个变成另一个),这使得 (->) r
的 Monad
实例完全等同于 Applicative
实例。
我试图通过在 Javascript 中实现它们的函数实例来提高我对 Applicative
和 Monad
的理解。我对 Haskell 的了解有限,我希望我的问题完全有意义。
这是我对 Functor
、Applicative
和 Monad
类型类的 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 中的求值顺序):
apply
:f(x)
...g(x)
...lambda(result of g)
...lambda
的结果
bind
:f(x)
...g(result of f)
...lambda(x)
...lambda
的结果
但是,bind
函数看起来很奇怪。为什么 f
和 g
以相反的方式嵌套?具体的Monad
行为(根据前一个效果决定下一个效果)在这个实现中是如何体现的?实际上 g(f(x)) (x)
看起来像一个带有翻转参数的函数组合,其中 g
是一个二元函数。
当我对一元和二元函数应用 apply
/bind
时,它们产生相同的结果。这没有多大意义。
函数的 monad 实例中的值具有某些固定类型 r
的类型 r -> a
。给定 (>>=)
的函数 (a -> (r -> b))
允许您根据当前值(函数 r -> a
)的结果选择下一个函数 return 。 f r
的类型为 a
,k (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 aref
andg
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
可以简单地将一个变成另一个),这使得 (->) r
的 Monad
实例完全等同于 Applicative
实例。