javascript - 为什么函数实例的绑定(bind)会为下一次计算提供原始值?

标签 javascript haskell functional-programming bind monads

作为一个对 Haskell 仅有模糊理解的函数式 Javascript 开发人员,我真的很难理解像 monad 这样的 Haskell 习语。当我查看函数实例的 >>=

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

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

// Javascript:

及其与Javascript的应用

const bind = f => g => x => g(f(x)) (x);

const inc = x => x + 1;

const f = bind(inc) (x => x <= 5 ? x => x * 2 : x => x * 3);


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

monadic 函数 (a -> (r -> b))(或 (a -> m b))提供了一种根据以下条件选择下一个计算的方法之前的结果。更一般地说,monadic 函数及其相应的 bind 运算符似乎使我们能够定义函数组合在特定计算上下文中的含义。

更令人惊讶的是,monadic 函数没有将先前计算的结果提供给后续计算。相反,原始值被传递。我希望 f(2)/f(5) 产生 6/18,类似于正常功能组成。这种行为是否特定于作为 monad 的函数?我误解了什么?

最佳答案

我认为您的困惑来自使用过于简单的函数。特别是,你写

const inc = x => x + 1;

它的类型是一个函数,它返回与输入相同的空间中的值。比方说 inc正在处理整数。因为它的输入和输出都是整数,如果你有另一个函数foo采用整数,很容易想象使用 inc输出作为 foo输入 .

不过,现实世界包含更多令人兴奋的功能。考虑函数 tree_of_depth它接受一个整数并创建一个该深度的字符串树。 (我不会尝试实现它,因为我对 javascript 的了解还不够多,无法完成令人信服的工作。)现在突然之间,很难想象传递 tree_of_depth 的输出。作为 foo 的输入, 自 foo期待整数和 tree_of_depth正在生产树木,对吧?我们唯一可以传递给foo的东西是 tree_of_depth输入 ,因为那是我们唯一的整数,即使在运行 tree_of_depth 之后也是如此.

让我们看看它是如何在绑定(bind)的 Haskell 类型签名中体现的:

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

这表示 (>>=)接受两个参数,每个参数都起作用。第一个函数可以是您喜欢的任何旧类型——它可以采用 r 类型的值。并产生 a 类型的值.特别是,您不必 promise ra完全一样。但是一旦你选择了它的类型,然后是 (>>=) 的下一个函数参数的类型。受约束:它必须是两个类型相同的参数的函数 ra和以前一样。

现在您可以明白为什么我们必须传递类型相同的值 r对于这两个函数:第一个函数产生一个 a ,不是更新的 r , 所以我们没有其他 r 类型的值传递给第二个函数!与您的情况不同 inc ,其中第一个函数恰好也产生一个r ,我们可能正在生产其他一些非常不同的类型。

这解释了为什么 bind 必须按原样实现,但可能没有解释为什么这个 monad 很有用。在其他地方有关于这一点的写作。但是规范的用例是配置变量。假设在程序启动时你解析了一个配置文件;然后对于程序的其余部分,您希望能够通过查看来自该配置的信息来影响各种功能的行为。在所有情况下,使用相同的配置信息都是有意义的——它不需要更改。然后这个 monad 变得有用:您可以有一个隐式配置值,并且 monad 的绑定(bind)操作确保您正在排序的两个函数都可以访问该信息,而无需手动将其传递给两个函数。

附言你说

It is all the more surprising that the monadic function doesn't supply the result of the previous computation to the subsequent one.

我觉得有点不精确:事实上在m >>= f , 函数 f得到两个 m 的结果(作为它的第一个参数)原始值(作为它的第二个参数)。

关于javascript - 为什么函数实例的绑定(bind)会为下一次计算提供原始值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40473118/

相关文章:

javascript - 使用 Javascript 从 URL 获取 Base64 图像

javascript - 如何以 Angular 形式将地址或任何字段复制到另一个字段?

javascript - 循环遍历缺少索引的数组的最快方法

haskell - Haskell 中更好的数据流读取

Haskell - 有一个函数返回一个空字符

types - 是否有静态类型的函数语言?

go - 如何将接口(interface)方法传递给函数?

javascript - 是否可以知道我的 AngularJS HTML 是否引用了不存在的 $scope 值?

if-statement - Haskell - do block 中的语法(使用 IO)

haskell - Where 子句的最佳实践