如何分享延续的中间结果?

bob*_*bob 8 javascript continuations haskell functional-programming memoization

请注意,即使此问题中的示例是用Javascript编码的,其基本概念在Haskell中也是常见的,而我虽然更喜欢用Javascript表达自己,但我也很欣赏Haskell中的答案。

在Javascript中,我根据单子原理使用CPS处理异步计算。但是,为了简单起见,我将对这个问题使用正常的延续单子。

一旦我的连续作文增长,我就会发现自己处于需要获得这些作文中间结果的情况。由于必须使用Javascript,因此很容易将此类结果存储在变量中,并在以后访问它们。但是由于我们在谈论连续性,因此访问中间结果意味着调用函数并多次访问它们意味着大量的重新评估。

这似乎非常适合记忆。但是,如果那个函数不返回任何东西而只调用其延续(以及顺便说一句),我该如何记住一个函数的返回值(正如我之前提到的那样,我使用异步函数也不会在Javascript事件循环的当前周期中返回任何东西) )。

似乎我必须提取正确的延续。也许可以通过shift/ 分隔定界符来实现reset,但是我不知道如何应用这些组合器。这个问题可能并没有那么难解决,我只是对持续传递风格的魔幻之地感到困惑……所以请纵容我。

这是ContJava语言中没有备忘的简化示例:

const taggedLog = tag => s =>
  (console.log(tag, s), s);

const id = x => x;

const Cont = k => ({
  runCont: k,
  [Symbol.toStringTag]: "Cont"
});

const contAp = tf => tx =>
  Cont(k => tf.runCont(f => tx.runCont(x => k(f(x)))));

const contLiftA2 = f => tx => ty =>
  contAp(contMap(f) (tx)) (ty);

const contOf = x => Cont(k => k(x));

const contMap = f => tx =>
  Cont(k => tx.runCont(x => k(f(x))));
                                  
const contReset = tx => // delimited continuations
  contOf(tx.runCont(id));

const contShift = f => // delimited continuations
  Cont(k => f(k).runCont(id));

const inc = contMap(x => taggedLog("eval inc") (x + 1));
const inc2 = inc(contOf(2));
const inc3 = inc(contOf(3));
const add = contLiftA2(x => y => taggedLog("eval add") (x + y));
const mul = contLiftA2(x => y => taggedLog("eval mul") (x * y));

const intermediateResult = add(inc2) (inc3);

mul(intermediateResult) (intermediateResult).runCont(id);

/*
  should only log four lines:
  eval inc 3
  eval inc 4
  eval add 7
  eval mul 49
*/
Run Code Online (Sandbox Code Playgroud)

Ber*_*rgi 8

您的问题似乎是您Cont还没有monad实现。这样,访问以前的结果非常简单-它们只是嵌套的连续回调的范围(作为常量):

const contChain = tx => f =>
  Cont(k => tx.runCont(r => f(r).runCont(k)));

contChain( add(inc2) (inc3), intermediateResult => {
  const intermediateCont = contOf(intermediateResult);
  return mul(intermediateCont) (intermediateCont);
}).runCont(id);
Run Code Online (Sandbox Code Playgroud)

(当然,您所有的函数都已经被提升并且将Cont值作为参数是有点奇怪的-他们不应该那样做,而只是将其作为值的函数)return Cont


您在Haskell中的代码:

import Control.Monad.Cont
import Control.Applicative

let inc = liftA (+1)
let inc2 = inc $ return 2
let inc3 = inc $ return 3
let add = liftA2 (+)
let mul = liftA2 (*)

(`runCont` id) $ add inc2 inc3 >>= \intermediateResult ->
  let intermediateCont = return intermediateResult
  in mul intermediateCont intermediateCont
-- 49

{- or with do notation: -}

(`runCont` id) $ do
  intermediateResult <- add inc2 inc3
  let intermediateCont = return intermediateResult
  mul intermediateCont intermediateCont
-- 49
Run Code Online (Sandbox Code Playgroud)

(我没有使用monad变压器taggedLog产生副作用)