Справа в тому, що насправді не так багато свободи в плані кодування функцій. Ось основні варіанти:
Переписування терміна: ви зберігаєте функції як їх абстрактні синтаксичні дерева (або якесь кодування їх. Коли ви викликаєте функцію, ви вручну перетинаєте дерево синтаксису, щоб замінити його параметри аргументом. Це легко, але жахливо неефективно з точки зору часу та простору .
Закриття: у вас є спосіб представити функцію, можливо, синтаксичне дерево, швидше машинний код. І в цих функціях ви якось посилаєтесь на свої аргументи шляхом посилання. Це може бути зміщення вказівника, це може бути ціле число або індекс De Bruijn, це може бути ім'я. Тоді ви представляєте функцію як закриття : функція "інструкції" (дерево, код тощо) в парі зі структурою даних, що містить усі вільні змінні функції. Коли функція реально застосовується, вона якось знає, як шукати вільні змінні в її структурі даних, використовуючи середовища, арифметику вказівника тощо.
Я впевнений, що є й інші варіанти, але це основні, і я підозрюю, що майже кожен інший варіант буде варіантом або оптимізацією основної структури закриття.
Так, з точки зору продуктивності, закриття майже загальнозміцніли краще, ніж термінове переписування. З варіацій, що краще? Це сильно залежить від вашої мови та архітектури, але я підозрюю, що "машинний код із структурою, що містить вільні параметри", є найбільш ефективним. У ньому є все, що потрібна функції (інструкції та значення), і більше нічого, і дзвінки не в кінцевому підсумку роблять великі термінові обходи.
Мене цікавлять як поточний алгоритм кодування, який використовують популярні функціональні мови (Haskell, ML)
Я не фахівець, але я на 99% більшість ароматизаторів ML використовую певні варіації закриттів, які я описую, хоча з певними оптимізаціями, ймовірно. Дивіться це на (можливо, застарілу) перспективу.
Haskell робить щось дещо складніше через ледачу оцінку: він використовує безперервне переписування графіків Tagless .
а також у найбільш ефективному, що може бути досягнуто.
Що найефективніше? Не існує жодної реалізації, яка була б найбільш ефективною для всіх вхідних даних, тому ви отримуєте реалізацію, яка є ефективною в середньому, але кожна матиме найкращі результати в різних сценаріях. Тож немає визначеного рейтингу найбільш ефективних або найменш ефективних.
Тут немає ніякої магії. Щоб зберігати функцію, потрібно якось зберігати її вільні значення, інакше ви кодуєте менше інформації, ніж сама функція. Можливо, ви можете оптимізувати деякі вільні значення за допомогою часткової оцінки, але це ризиковано для продуктивності, і ви повинні бути обережними, щоб це завжди зупинялося.
І, можливо, ви можете використовувати якийсь стиснення або розумний алгоритм, щоб отримати космічну ефективність. Але тоді ви або торгуєте часом на простір, або потрапляєте в ситуацію, коли ви оптимізували для одних випадків і сповільнювали для інших.
Ви можете оптимізувати для загального випадку, але те, що є загальним випадком , може змінити мову, область застосування і т. Д. Тип коду, швидкий для відеоігри (хрускіт числа, щільні петлі з великим входом), ймовірно, відрізняється, ніж що швидко для компілятора (обхід дерева, робочі списки тощо).
Бонусний бал: чи існує таке кодування, яке відображає функції закодованих цілих чисел до нативних цілих чисел (коротких, int тощо у C). Чи можливо це навіть?
Ні, це неможливо. Проблема полягає в тому, що лямбда-числення не дозволяє вам переглядати терміни. Коли функція бере аргумент того ж типу, що й церковна цифра, її потрібно мати можливість викликати, не вивчаючи точне визначення цього числа. Це те, що стосується кодування Церкви: єдине, що ви можете зробити з ними, це назвати їх, і ви можете імітувати все корисне з цим, але не без витрат.
Що ще важливіше, цілі числа займають усі можливі бінарні кодування. Отже, якби лямбда були представлені як їх цілі числа, у вас не було б способу представити лямбди без церкви! Або ви введете прапор, щоб позначити, лямбда - це цифра чи ні, але тоді будь-яка ефективність, яку ви хочете, ймовірно, вийде з вікна.
EDIT: З моменту написання цього запитання мені стало відомо про третій варіант реалізації функцій вищого порядку: дефункціоналізація . Тут кожен виклик функції перетворюється на велику switch
заяву залежно від того, яка абстракція лямбда була надана як функція. Вигода тут полягає в тому, що це ціла трансформація програми: ви не можете складати частини окремо, а потім зв’язувати разом таким чином, оскільки вам потрібно мати повний набір лямбда-абстракцій раніше часу.