Реализация Оптимизации Tail Call в Javascript Engine - PullRequest
0 голосов
/ 16 февраля 2019

По состоянию на февраль 2019 года в Chrome версии 71.0.3578.98 на Mac, следующая программа выбрасывает Uncaught RangeError: Maximum call stack size exceeded error. при счете 16516.

const a = x => {
  console.log(x)
  a(x + 1)
}

a(1)

Я немного погуглил,но не смог найти ни одной статьи, обсуждающей Chrome или другую поддержку браузера для Tail Call Optimization (TCO) или какие-либо будущие планы по ее реализации.

Мои два вопроса:

  1. Поддерживается ли в настоящее время TCO в Chrome или любом другом браузере или движке Javascript
  2. Планируется ли внедрение TCO в ближайшем будущем влюбой движок Javascript

Посты, которые я обнаружил, в основном старые (2016 или более ранние) или просто сбивают с толку.например, https://www.chromestatus.com/feature/5516876633341952

Ответы [ 2 ]

0 голосов
/ 16 февраля 2019

TCO, или, скорее, Tail Call Устранение в JavaScript - также часто упоминаемый как Собственные Tail Calls (PTC) в обсуждениях - это длинная и грустная история.

ВокругВ 2011 году TC39 (комитет по стандартам JavaScript) решил принять обязательный TCE для готовящегося стандарта ES6 с согласия всех основных поставщиков браузеров.

В 2015 году новый стандарт был официально принят под названием EcmaScript 2015.На данный момент ни один браузер на самом деле не внедрил TCE, в основном потому, что в ES2015 было слишком много новых функций, которые считались более важными для выхода.(Сегодняшний процесс для предложений по функциям JS и их принятия, который включает требование двух реализаций в производственных двигателях, еще не существовал для ES6.)

В начале 2016 года и Safari, и Chrome внедрили TCE.Safari объявила о его отправке, в то время как Chrome держал его под флагом «Экспериментальная функция».Другие браузеры (Firefox и Internet Explorer / Edge) тоже начали его изучать и подумали.Обсуждение развилось, является ли это жизнеспособной особенностью в конце концов.У Edge были проблемы с его эффективной реализацией для Windows ABI, Firefox был обеспокоен тем, как разработчики обращались к «пропущенным» вызовам из стековых трасс (проблема, которая уже подробно обсуждалась в 2011 году).

В попытке решить проблемунекоторые из этих проблем при спасении функции хвостового вызова, некоторые участники, включая команды Chrome и Edge, предложили сделать хвостовые вызовы явными , то есть требовать, чтобы операторы возврата были аннотированы дополнительным ключевым словом для выбора в хвостевызов семантики.Эти так называемые « синтаксические хвостовые вызовы » (STC) были реализованы в Chrome в качестве доказательства концепции.

На совещании TC39 в мае 2016 года проблема хвостовых вызовов широко обсуждалась почтицелый день без разрешения.Firefox и Edge дали понять, что они не будут реализовывать TCE, как указано в стандарте.Члены Firefox предложили убрать его.Safari и Chrome не согласились с этим, и команда Safari ясно дала понять, что не намерена отсылать ТВК.Предложение о синтаксических хвостовых вызовах также было отклонено, особенно Safari.Комитет был в тупике.Вы можете прочитать примечания к заседанию этого обсуждения .

Технически, насколько я знаю, этот тупик все еще существует сегодня.На практике, однако, хвостовые вызовы для JavaScript в значительной степени мертвы, и неясно, вернутся ли они когда-нибудь.По крайней мере, к такому выводу пришла команда Chrome после катастрофической встречи, которая привела к решению убрать реализацию хвостовых вызовов из Chrome, чтобы упростить двигатель и предотвратить гниение битов.Они все еще доступны в Safari.

Раскрытие информации: Я был членом TC39 и команды Chrome / V8 до 2017 года, поэтому мои взгляды могут быть предвзятыми.

0 голосов
/ 16 февраля 2019

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

const a = x => {
  if(x > 500000) {
      console.log(x);
      return; 
  }
  return ()=> a(x + 1); //you return a function, it hasn't been called yet
}

const trampoline = fn => (...args) => {
  let result = fn(...args)
  //repeatedly call the function till you hit your base case
  while (typeof result === 'function') {
    result = result();
  }
  
  return result;
}

var t = trampoline(a);
t(1);
...