Чому функціональні мови програмування вимагають збирання сміття?


14

Що зупиняє ghc від перекладу Haskell в конкатенативну мову програмування, таку як комбінаційна логіка, а потім просто використання розподілу стеків для всього? Згідно з Вікіпедією, переклад з обчислення лямбда в комбінаторну логіку є тривіальним, а також, конкатенативні мови програмування можуть покладатися виключно на стек для розподілу пам'яті. Чи можливо зробити цей переклад і таким чином виключити збирання сміття для таких мов, як Haskell та ocaml? Чи є в цьому недоліки?

EDIT: переміщено сюди /programming/39440412/why-do-functional-programming-languages-require-garbage-collection


Cat Мова програмування виглядає як приклад функції, на основі стека мови.
Петро Пудлак

1
Це питання не є дослідницьким рівнем, оскільки збирання сміття охоплюється на бакалаврських курсах з мов програмування (як і потреба в ньому). Перейдіть на сторінку cs.stackexchange.com
Андрій Бауер

Моя помилка. Чи знаєте ви відповідь на моє запитання?
Микола Грашевський

5
Я думаю, що на це питання потрібно відповісти на рівні дослідницького рівня, оскільки я пам’ятаю, що боровся з ним і в роки свого навчання: все мовою, як Haskell, схоже на програму функції, яка живе на стеці. Я думаю, що пояснення того, чому закриття необхідне, чому вони живуть на купі, і, можливо, пов’язані з цим "дані, що уникають області функцій", дадуть дуже інформативну відповідь (яку я не впевнений, що я кваліфікований, щоб дати, на жаль).
коді

2
λ

Відповіді:


16

Усі наступні коментарі ґрунтуються на виборі стандартної стратегії впровадження, використовуючи закриття для представлення значень функцій та порядку оцінки за закликом за значенням:

  1. Для чистого обчислення лямбда збирання сміття не потрібно. Це тому, що неможливо формувати цикли в купі: кожне новопризначене значення може містити лише посилання на раніше виділені значення, і тому графік пам'яті утворює DAG - тому підрахунок посилань достатній для управління пам'яттю.

  2. Більшість реалізацій не використовують підрахунок посилань з двох причин.

    1. Вони підтримують форму типу вказівника (наприклад, refконструктор типу в ML), і тому можна сформувати справжні цикли в купі.
    2. Довідковий підрахунок набагато менш ефективний, ніж вивезення сміття
      • йому потрібно багато додаткового простору для збереження відліків та
      • оновлення підрахунків зазвичай є марною роботою, і
      • оновлення підрахунків створюють купу суперечок щодо запису, що вбиває паралельну продуктивність.
  3. Лінійно-введені мови можуть усунути кількість посилань (фактично тому, що підрахунки дорівнюють 0-1: або значення має єдине посилання на нього, або воно мертве і може бути звільнене).

  4. Однак розподілу стеків все ще недостатньо. Це тому, що можна сформувати значення функцій, які посилаються на вільні змінні (тобто нам потрібно реалізувати закриття функції), якщо ви виділите речі в стеку, то живі значення можуть бути переплетені з мертвими значеннями, і це призведе до неправильної асимптотики використання простору.

  5. Ви можете отримати правильну асимптотику, замінивши стек на "стек спагетті" (тобто реалізуйте стек як пов'язаний список у купі, щоб ви могли вирізати мертві кадри за необхідності).

  6. Якщо ви хочете реальної дисципліни стека, ви можете використовувати системи типів на основі "впорядкованої логіки" (по суті лінійних типів мінус обмін).


2
Хіба не основна причина (2) полягає в тому, що - навіть без помітних побічних ефектів - реалізація хоче мати ефективного оператора для (взаємної) рекурсії, тобто таку, яка насправді формує цикл у купі?
Андреас Росберг

@andreasrossberg: Я думав про те, щоб згадати це, але покинув це, оскільки ви можете використовувати комбінатор y для рекурсії.
Neel Krishnaswami
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.