Вирішено Впровадження лінивих функціональних мов haskell
При реалізації ледачої функціональної мови необхідно зберігати неоцінені значення, що оцінюються, лише коли це потрібно.

Однією із проблем ефективної реалізації, як обговорюється, наприклад, у La Veule Tagless G-machine, є те, що цю оцінку потрібно проводити лише один раз для кожного обману, а запити на "Доступ повинні повторно використовувати розраховане значення - не робити так це призвело б до принаймні квадратичного уповільнення (можливо, експоненціального? Я не впевнений, що це над моєю головою.)
Я шукаю простий приклад реалізації, яка працює легко (на відміну від індустріальної сили реалізації, такої як GHC, яка розроблена для ефективності за рахунок простоти). Я натрапив на minihaskell за адресою http://www.andrej.com/plzoo/, що містить наступний код.
Оскільки він отримав прізвисько "ефективний виконавець", я припускаю, по суті, виконувати кожну оцінку один раз і зберігати розраховане значення для повторного використання, але мені важко бачити, де і як? 'Або' Що; Я бачу лише одне твердження про призначення в самому інтерпретаторі, і це, здається, не перезаписує частину хитромудрого запису.
Отже, моє запитання полягає в тому, чи справді це перекладач, який виконує таке кешування, і якщо так, то де і як? (А якщо ні, то яка найпростіша реалізація з існуючих?)
Відповіді
Основними є файли: avis,! R, r: = v. Кожного разу, коли ми шукаємо змінну середовища, ми повертаємо запис, який ми орієнтуємо, щоб побачити, чи це хитрюга. Якщо це хитрість, ми оцінюємо це, а потім зберігаємо результат. Ми створюємо thunks під час застосування (зверніть увагу на виклик конструктора ref), рекурсивні визначення та узгодження шаблонів, оскільки це конструкції, які прив'язують змінні.
Ось два перекладачі за викликом за потребою; один у Хаскелі, а другий у Діаграмі. Ключ у тому, що ви можете призупинити всередині процедур оцінки n аргументів (бітів). Якщо вашою мовою хосту є виклик за необхідністю (Haskell) або виклик за значенням (схема, ML), лямбда-форми вважаються значеннями, тому все, що знаходиться під лямбда, буде оцінюватися, доки не буде застосовано.
Отже, коли до аргументу застосовується інтерпретація функції, ви просто обертаєте неоцінене синтаксичне подання аргументу новим дурнем. Отже, коли ви натрапляєте на змінну, ви заглядаєте в оточення і швидко оцінюєте потік, надаючи вам значення аргументу
Просто діставшись до цього пункту, ваш перекладач стає лінивим, оскільки аргументи не оцінюються, поки вони не звикли; це виклик перекладача. Однак, як ви зазначаєте, ефективний метод лінивої оцінки цих аргументів лише один раз; така мова називається з необхідності. Для досягнення цієї ефективності оновлення середовища замість того, щоб містити thunk, що містить значення аргументу, а не весь аргумент виразу.
Перший інтерпретатор тут знаходиться в Haskell і дуже схожий на код ML, який ви вставили. Звичайно, проблеми в Haskell: 1) не тривіально реалізовувати лінощі, завдяки вбудованій ліні Haskell, і 2) сваритися через побічні ефекти в коді. Haskell IORef використовуються для дозволу середовища оновлення.
Другий перекладач в режимі, де єдиним справжнім шедевром є макрос, що відповідає шаблону Олега. Мені набагато легше зрозуміти, звідки береться лінь із схематичної версії. Функції box дозволяють оновлювати середовище; У Schéma це розуміють, але я включив визначення, які повинні працювати для інших.