如何使用非类型化语言中的 Reader Monad 在整个组合中隐式地线程化参数?

2024-04-22

我知道裸读者单子仅包含两个功能:

const chain = g => f => x => f(g(x)) (x);
const of = x => _ => x;

但我对它如何工作或如何应用没有任何直觉。知道 is 用于在整个组合中隐式地线程化参数并没有多大帮助。


读者 monad 很难掌握,因为它使用的功能相当普通(函数应用)并且它的应用有点不直观。怎么能f => g => x => f(g(x)) (x)当两个参数最初相同时有用吗?让我们从一个简单的例子开始:

常量公司 = x => x + 1; const sqr = x => x * x; const add = x => y => x + y;

取决于位置add由于数量存在偏差,这些函数无法开箱即用地组合。从更广义的意义上来说,你可以说add需要一个额外的参数并且inc/sqr需要了解这种情况。

Reader Monad 可用于在此类场景中获得更大的灵活性。在无类型设置中,Reader 值只是存储在普通旧 JS 对象中的函数:

const Reader = f => ({
  [Symbol.toStringTag]: "Reader",
  run: e => f(e) // eta expanded for clarity
});

const incR = x => Reader(e => x + 1);
const sqrR = x => Reader(e => x * x);
const addR = x => Reader(e => x + e);

初始示例中的功能现在已调整为新的 Reader“类型”。e决定性的因素就是环境。它是由 Reader Monad 处理的隐式额外参数。e可以是一个标量值或一个复合值来编码多个额外的参数。如你看到的e仅由以下人员使用addR并被其他人忽视。

这些函数如何组合呢?显然,正常的函数组合不再起作用了。我们需要一个构造来编码组合如何与 Reader 类型一起工作。这正是 monad 结构给我们带来的:

const Reader = f => ({
  [Symbol.toStringTag]: "Reader",
  run: e => f(e)
});

const id = x => x;

Reader.chain = mg => fm => Reader(e => fm(mg.run(e)).run(e));
Reader.of = x => Reader(_ => x);
Reader.ask = Reader(id);

const r = Reader.chain(incR(2)) (x => // Reader {run: f}
  Reader.chain(sqrR(x)) (y =>
    Reader.chain(addR(y)) (z =>
      Reader(e => [e, z]))));

I use Reader.chain用于组合和提供价值2进入构图。结果r的计算是Reader {run: f}。这给我们提供了构图尚未评估的线索。缺了点什么。对,环境论。让我们通过它:

r.run(5) // [5, 14]

该组合产生原始环境参数e以及计算结果。这是未纠缠的计算:

   2 + 1 = 3
   3 * 3 = 9
   9 + 5 = 14
//     ^env

Reader.chain构建一个嵌套函数调用堆栈,这是对仅在传递环境参数时才进行计算的计算的描述。

如果我们想要怎么办sqrK以基于e还有?只是Reader.ask环境:

const r2 = Reader.chain(incR(2)) (x =>
  Reader.chain(Reader.ask) (e2 =>
//             ^^^^^^^^^^
    Reader.chain(sqrR(e2 % 2 === 1 ? 1 : x)) (y =>
      Reader.chain(addR(y)) (z =>
        Reader(e => [e, z])))));

r2.run(5); // [5, 6]
r2.run(4); // [4, 13]

所需要的只是一个额外的Reader.chain(Reader.ask) call. e2为后续的延续提供环境。

这就是它的要点。这是许多单子样板文件,以换取隐式线程参数。我想说,如果您需要传递配置并且已经使用另一个 monad 进行组合,那么它仍然很有用。不同类型的 Monad 不能直接组合,但您可以使用 monad 变压器堆栈。

这是给定示例的一个可运行示例,包括infix平面组合语法的运算符:

const r_ = infix(
  incR,
  kompR,
  sqrR,
  kompR,
  addR) (2);

Reader.chain(r_) (z => Reader(e => [e, z])).run(5);

或者,您可以使用生成器语法糖来获得更命令式的编码体验:https://stackoverflow.com/a/65060136/5536315 https://stackoverflow.com/a/65060136/5536315.

const Reader = f => ({
  [Symbol.toStringTag]: "Reader",
  run: e => f(e)
});

const id = x => x;
const log = x => console.log(x);

Reader.map = f => tg => Reader(e => f(tg.run(e)));
Reader.ap = tf => tg => Reader(e => tf.run(e) (tg.run(e)))
Reader.of = x => Reader(_ => x);
Reader.chain = mg => fm => Reader(e => fm(mg.run(e)).run(e));
Reader.ask = Reader(id);

const incR = x => Reader(e => x + 1);
const sqrR = x => Reader(e => x * x);
const addR = x => Reader(e => x + e);

const komp = ({chain}) => fm => gm => x => chain(fm(x)) (gm);
const kompR = komp({chain: Reader.chain});

const makeInfix = nestFirst => (...args) => {
  if (args.length === 0) throw new TypeError("no argument found");

  let i = 1, x = args[0];

  while (i < args.length) {
    if (i === 1) x = args[i++] (x) (args[i++]);
    else if (nestFirst) x = args[i++] (x) (args[i++]);
    else x = args[i++] (args[i++]) (x);
  }

  return x;
};

const infix = makeInfix(true);

const r = Reader.chain(incR(2)) (x =>
  Reader.chain(sqrR(x)) (y =>
    Reader.chain(addR(y)) (z =>
      Reader(e => [e, z]))));

r.run(5); // [5, 14]

const r2 = Reader.chain(incR(2)) (x =>
  Reader.chain(Reader.ask) (e2 =>
    Reader.chain(sqrR(e2 % 2 === 1 ? 1 : x)) (y =>
      Reader.chain(addR(y)) (z =>
        Reader(e => [e, z])))));

r2.run(5); // [5, 6]
r2.run(4); // [4, 13]

const r_ = infix(
  incR,
  kompR,
  sqrR,
  kompR,
  addR) (2);

log(Reader.chain(r_) (z => Reader(e => [e, z])).run(5)); // [5, 14]

const r2_ = infix(
  incR,
  kompR,
  x => Reader(e => e % 2 === 1 ? 1 : x * x),
  kompR,
  addR) (2);

log(Reader.chain(r2_) (z => Reader(e => [e, z])).run(5)); // [5, 6]
log(Reader.chain(r2_) (z => Reader(e => [e, z])).run(4)); // [4, 13]
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用非类型化语言中的 Reader Monad 在整个组合中隐式地线程化参数? 的相关文章

随机推荐