【发布时间】:2020-05-30 06:31:07
【问题描述】:
一元计算在 JS 中很快变得令人困惑:
const chain = fm => xs =>
xs.reduce((acc, x) => acc.concat(fm(x)), []);
const of = x => [x];
const main = xs => ys => zs =>
chain(x =>
x === 0
? []
: chain(y =>
chain(z => [[x, y, z]]) (zs)) (ys)) (xs);
console.log("run to completion",
main([1, 2]) (["a", "b"]) ([true, false]));
console.log("short circuiting",
main([0, 2]) (["a", "b"]) ([true, false]));
在 Haskell 中,do 表示法可用于隐藏嵌套的函数调用。但是,do 是 Javascript 缺乏的一种编译时技术。
生成器函数似乎很合适,但它们不适用于提供优先选择的 monad。所以我一直在寻找一个替代方案,最近想出了一种单子应用程序,以便稍微解开嵌套计算:
const chain = fm => xs =>
xs.reduce((acc, x) => acc.concat(fm(x)), []);
const of = x => [x];
const id = x => x;
const infixM3 = (w, f, x, g, y, h, z) =>
f(x_ =>
w(x_, w_ => g(y_ =>
w_(y_, w__ => h(z_ =>
w__(z_, id)) (z))) (y))) (x);
const mainApp = xs => ys => zs => infixM3(
(x, k) =>
x === 0
? []
: k((y, k) =>
k((z, k) => [[x, y, z]])),
chain, xs,
chain, ys,
chain, zs);
console.log("run to completion",
mainApp([1, 2]) (["a", "b"]) ([true, false]));
console.log("short circuiting",
mainApp([0, 2]) (["a", "b"]) ([true, false]));
涂抹器在中缀位置模仿链条,因此得名。它基于局部延续,因此提升函数需要一对分别由边界值和延续组成的参数。如果未应用延续,则计算短路。它看起来很复杂,但实际上是一个机械过程。
将显式版本与抽象版本进行比较,我认为这是在可读性方面的改进:
chain(x =>
x === 0
? []
: chain(y =>
chain(z => [[x, y, z]]) (zs)) (ys)) (xs);
infixM3(
(x, k) =>
x === 0
? []
: k((y, k) =>
k((z, k) => [[x, y, z]])),
chain, xs,
chain, ys,
chain, zs);
提升函数中的延续以及应用程序具有 arity 意识的事实令人困扰。此外,它看起来根本不像 do-notation。我们能否更接近类似于do 表示法的语法?
【问题讨论】:
-
我想你可以使用像 Babel 这样的预处理器来添加你想看到的语言特性;例如this do expression extension.
-
ClojureScript?你可以做宏,它会编译成 JS
-
不适用于原生 JS,但有很多工具可以让您轻松使用自己的语法 - 例如 sweetjs.org
标签: javascript functional-programming monads do-notation