Как реализовать сопрограмму, основанную на продолжениях с несколькими выстрелами? - PullRequest
0 голосов
/ 12 января 2019

Я недавно реализовал продолжения с разделителями в CPS с reset / shift:

// reset :: ((a -> a) -> a) -> (a -> r) -> r
reset = k => f => f(k(id));

// shift :: ((a -> r) -> (r -> r) -> r) -> (a -> r) -> r
shift = f => k => f(k) (id);

Изучая теорию, я понял следующие связи:

reset ~ function* // scope of the generator function
shift ~ yield

reset ~ async // scope of the asyn function
shift ~ await

Насколько я понимаю эту теорию, генераторы Javascript - это асимметричные сопрограммы первого класса без стеков.

  • асимметричный означает, что вызываемый генератор может уступать только своему вызывающему
  • без стеков означает, что генератор не может работать из вложенных функций
  • однократный выстрел означает, что генератор может вернуться из определенной позиции только один раз
  • первый класс означает, что объект генератора может передаваться как обычные данные

Теперь я хочу реализовать сопрограмму на основе reset / shift со следующими чертами:

  • stackful
  • серийная съемка
  • первый класс

При рассмотрении следующего надуманного примера

const id = x => x;
const mul = x => y => x * y;
const add = x => y => x + y;
const sub = x => y => x - y;

const reset = k => f => f(k(id));
const shift = f => k => f(k) (id);

const of = x => k => k(x);
const lift2 = f => tx => ty => k => tx(x => ty(y => k(f(x) (y))));

const k0 = lift2(sub)
  (reset
    (lift2(add) (of(3))
      (shift
        (k => of(mul(5) (2))))))
          (of(1)); // 9

const k1 = lift2(sub)
  (reset
    (lift2(add) (of(3))
      (shift
        (k => of(k(mul(5) (2)))))))
          (of(1)); // 12

const k2 = lift2(sub)
  (reset
    (lift2(add) (of(3))
      (shift
        (k => of(k(k(mul(5) (2))))))))
          (of(1)); // 15

console.log(k0(id));
console.log(k1(id));
console.log(k2(id));

кажется, что reset / shift уже соответствуют последним двум критериям, потому что продолжения с разделителями - это просто класс первого класса, составные функции, и я могу вызывать продолжение k так часто, как это требуется. Чтобы ответить на вопрос «почему», я хочу обойти следующее ограничение в связи с монадой списка. Верны ли эти предположения?

Даже если я до сих пор не допустил ошибок, я поражен сложностью задачи на данный момент. Я понятия не имею, как реализовать желаемую сопрограмму или даже с чего начать. Я также не нашел пример реализации. Я не ожидаю полной реализации, но, может быть, какое-то руководство поможет мне достичь цели.

Гол * * тысяча пятьдесят-одна Я хочу обойти следующее ограничение сопрограмм, реализованное генераторами Javascript: const arrMap = f => xs => xs.map(x => f(x)); const arrAp = fs => xs => fs.reduce((acc, f) => acc.concat(xs.map(x => f(x))), []); const arrChain = xs => fm => xs.reduce((acc, x) => acc.concat(fm(x)), []); const arrOf = x => [x]; const do_ = (of, chain) => it => { const loop = ({done, value}) => done ? value : chain(value) (x => loop(it.next(x))); return loop(it.next()); }; const z = function*() { const x = yield [1,2,3] return [x, x]; } console.log( arrChain([1,2,3]) (x => [x, x])); console.log( do_(arrOf, arrChain) (z()));

...