В чем заключается хитрость генерации кода Javascript с продолжениями? - PullRequest
3 голосов
/ 22 февраля 2012

Я ищу способ добавить в Javascript очень специфическую форму без вытеснения многопоточности. Javascript 1.7 в Mozilla поддерживает нативные сопрограммы, использующие yield, но я предпочитаю не использовать решение для браузера. Я видел, что есть несколько реализаций продолжений или сопрограмм, основанных на преобразовании аннотированного кода Javascript в простой Javascript. Вот некоторые примеры: StraifiedJS , Повествовательный Javascript и jwacs .

Мне не нужна полнофункциональная среда для имитации асинхронных вызовов Javascript; Мне просто нужно это для очень специфического использования, которое я хотел бы реализовать. Поэтому вышеперечисленные библиотеки для меня излишни.

Может ли кто-нибудь указать мне на основные "хитрости" (или хитрости), которые используют такие препроцессоры? Есть ли какой-то особый языковой взлом, который делает возможным продолжение в Javascript за счет создания дополнительного кода? Любая соответствующая ссылка приветствуется.

1 Ответ

10 голосов
/ 22 февраля 2012

Это стиль передачи продолжения .

Javascript - это Лисп, но в качестве синтаксиса носит одежду C.

Поскольку Javascript по своей сути является функциональным языком, он действительно сумасшедшийтрюки возможны, как продолжение прохождения стиля.Но эти уловки вызывают головную боль.

В заключение, продолжение - это концепция того, что делать дальше - сделать доступным как то, что вы можете вызвать, точно так же, как функцию.Я также иногда рассматриваю продолжение как сохраненный стек фреймов вызовов: вы можете сохранить стек вызовов функций в качестве состояния выполнения и вернуться или просто «вызвать» это состояние позже.

Кто-то продемонстрировал это путем преобразования кодав стиле прохождения продолжения вы можете получить силу продолжений.Вот Это Да!Это действительно впечатляет:

Просто преобразование исходного кода, и у вас есть возможность продолжения.

Теперь проблема с Javascript заключается в его синтаксисе языка Си.Трудно сделать преобразование исходного кода с синтаксисом Си.Было бы проще с синтаксисом Lisp, но все же утомительно и подвержено ошибкам.

Нам повезло, что некоторые действительно гениальные люди сделали тяжелую работу за нас.Эта тяжелая работа влечет за собой использование синтаксического анализатора Javascript, потому что действительно означает это преобразование?В итоге это означает переупорядочение последовательности операций таким образом, чтобы сначала было выполнено то, что действительно выполняется вначале.

f(g(a + x))

Сначала выполняется добавление a + x, затем вызов функции g(), а затем f(),Есть три подвыражения.В преобразовании CPS результат подвыражений передается в продолжение.Это включает в себя создание многих внутренних вспомогательных функций в качестве временных продолжений.Это может быть сложным и утомительным, как мы увидим ниже.

В http://en.wikipedia.org/wiki/Continuation-passing_style пример функции

(define (pyth x y)
  (sqrt (+ (* x x) (* y y))))

преобразуется в

(define (pyth& x y k)
  (*& x x (lambda (x2)
      (*& y y (lambda (y2)
               (+& x2 y2 (lambda (x2py2)
                          (sqrt& x2py2 k))))))))

Это соответствует Javacript

function pyth(x, y) {
    return Math.sqrt(x * x + y * y);
}

, но *, + и Math.sqrt () не являются функциями, в которых CPS будет иметь смысл.

Но давайте предположим для примера, что *, + и Math.sqrt () являются веб-сервисами.Это важно, потому что вызовы веб-службы Javascript асинхронны .Каждый, кто работал с асинхронными вызовами, знает, как сложно объединить результаты этих вызовов.С библиотекой или генераторами предварительной обработки становится легче справляться с асинхронными результатами.

Итак, давайте напишем пример по-другому:

function pyth(x, y) {
    return sqrt(add(mul(x, x), mul(y, y)));
}

тогда преобразование CPS может выглядеть следующим образом:

function pyth_cps(x, y, k) {
  mul_cps(x, x, function(x2) {
    mul_cps(y, y, function(y2) {
      add_cps(x2, y2, function(x2py2) {
        sqrt_cps(x2py2, k);
      })
    })
  });
}

Мы видим, что результирующий код вырван наизнанку и сделан положительно нечитаемым.Каждая функция преобразуется.Все они получают магический параметр k.Это продолжение.В javascript это функция, которая получает результат операции.Где-то глубоко в стеке вызовов k вызывается.В нашем примере в CPS-преобразовании sqrt () здесь не показано.

Также обратите внимание, что преобразованные CPS-функции никогда не возвращаются.Они просто называют продолжение с результатом расчета.Это может привести к исчерпанию стека.Все преобразователи Javascript CPS должны справиться с этим.В Схеме это не обязательно, потому что все вызовы являются хвостовыми вызовами.Конечный вызов не нуждается в дополнительном фрейме вызова.В Javascript необходим батут или похожая техника.Вместо непосредственного вызова продолжения вызовите помощника и передайте результат и продолжение ему.Помощник работает в бесконечном цикле, всегда вызывая и возвращая и избегая исчерпания стека.

Итак, а почему этот CPS дает нам возможность продолжения?Это потому, что продолжение - это просто то, что нужно сделать дальше.Если мы всегда переносим эту концепцию чего-то, что должно быть сделано дальше, в качестве дополнительного параметра k и всегда передаем ему результат текущего выражения, то мы реализовали эту концепцию в коде.Однако, как мы видели, это «всегда носить с собой» утомительно осознавать.

Это крутая цена, даже если мы позволим делать препроцессор исходного кода делать тяжелую работу.Почему мы должны использовать продолжения?Можно абстрагировать поток управления.Seaside, платформа веб-приложений, использует продолжения, чтобы абстрагировать поток запросов браузера без сохранения состояния.Взаимодействие с пользователем можно смоделировать кратко - человек больше не мыслит в запросах, а в потоках взаимодействияЭто только один из многих примеров силы продолжения.Многим людям эта сила кажется странной и несколько пугающей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...