Чи є функціональна мова, яка дозволяє використовувати семантику стека - автоматичне детерміноване знищення в кінці області дії?
Чи є функціональна мова, яка дозволяє використовувати семантику стека - автоматичне детерміноване знищення в кінці області дії?
Відповіді:
Не те, про що я знаю, хоча я не фахівець з функціонального програмування.
В принципі це здається досить важким, тому що значення, повернені з функцій, можуть містити посилання на інші значення, створені (у стеці) в межах тієї ж функції, або так само легко передаватися як параметр або посилатися на щось передане в як параметр. В C цією проблемою вирішується, дозволяючи, щоб вивішені покажчики (а точніше, невизначена поведінка) могли виникнути, якщо програміст не виправить справи. Це не таке рішення, яке схвалюють дизайнери функціональної мови.
Однак можливі рішення. Одна ідея полягає в тому, щоб зробити час життя значення частиною типу значення разом із посиланнями на нього та визначити правила, засновані на типі, що запобігають поверненню виділених стеком значень або посиланням на щось, повернене з функція. Я не працював над наслідками, але підозрюю, що це буде жахливо.
Для монадичного коду існує ще одне рішення, яке (фактично або майже) є монадичним і могло б дати своєрідну детерміновано-знищену IORef. Принцип полягає у визначенні "гніздових" дій. У поєднанні (за допомогою асоціативного оператора) вони визначають потік керування введенням - я думаю, що "XML-елемент", з лівою більшістю значень, що забезпечують зовнішню пару початкового і кінцевого тегів. Ці "теги XML" якраз і визначають порядок монадичних дій на іншому рівні абстракції.
У якийсь момент (у правій частині ланцюга асоціативної композиції) вам потрібен якийсь термінатор, щоб закінчити гніздування - щось заповнити отвір посередині. Необхідність термінатора - це те, що, ймовірно, означає, що оператор складу гніздової композиції не є монадичним, хоча, знову ж таки, я не зовсім впевнений, оскільки не опрацьовував деталі. Оскільки все, що застосовує термінатор, - це перетворити діючу дію в ефективно складену нормальну монадію, а може й ні - це не обов'язково впливає на оператора гніздової композиції.
Багато з цих спеціальних дій мали б нульовий крок "кінцевий тег" і прирівнювали б крок "початку-тегу" до деякої простої монадичної дії. Але деякі представляють декларації змінних. Вони представляли б конструктор із початковим тегом та деструктор із кінцевим тегом. Отже, ви отримуєте щось на кшталт ...
act = terminate ((def-var "hello" ) >>>= \h ->
(def-var " world") >>>= \w ->
(use-val ((get h) ++ (get w)))
)
Переводячи до монадійної композиції з наступним порядком виконання, кожен тег (не елемент) стає нормальною монадичною дією ...
<def-var val="hello"> -- construction
<def-var val=" world> -- construction
<use-val ...>
<terminator/>
</use-val> -- do nothing
</def-val> -- destruction
</def-val> -- destruction
Такі правила можуть дозволити реалізацію RAII у стилі C ++. Посилання, подібні до IORef, не можуть уникнути своєї сфери застосування, з аналогічних причин, чому нормальні IORefs не можуть уникнути монади - правила асоціативного складу не дають змоги уникнути посилання.
EDIT - Я майже забув сказати - тут є певна область, про яку я не впевнений. Важливо переконатися, що зовнішня змінна не може посилатися на внутрішню, в основному, тому повинні бути обмеження, що ви можете зробити з цими посиланнями, схожими на IORef. Знову ж таки, я не проробив усі деталі.
Отже, конструкція може, наприклад, відкрити файл, який знищує закриття. Конструкція може відкрити розетку, де руйнування закривається. В основному, як і в C ++, змінні стають менеджерами ресурсів. Але на відміну від C ++, немає об'єктів, виділених купу, які неможливо автоматично знищити.
Хоча ця структура підтримує RAII, вам все одно потрібен сміттєзбірник. Хоча вкладена дія може виділити та звільнити пам’ять, розглядаючи її як ресурс, все ж є всі посилання на (потенційно спільні) функціональні значення всередині цього фрагмента пам'яті та інших місць. Зважаючи на те, що пам'ять можна було просто виділити на стек, уникаючи необхідності без нагромадження, справжнє значення (якщо воно є) має для інших видів управління ресурсами.
Тому цього досягається - відокремити управління ресурсами в стилі RAII від управління пам'яттю, принаймні у випадку, коли RAII базується на простому вкладенні. Вам все ще потрібен сміттєзбірник для управління пам’яттю, але ви отримуєте безпечне та своєчасне автоматичне детерміноване очищення інших ресурсів.
shared_ptr<>
), ви все одно зберігаєте детерміновані руйнування. Єдине, що є складним для RAII, - це циклічні посилання; RAII працює чисто, якщо графік власності - це спрямований ациклічний графік.
Якщо ви вважаєте, що C ++ є функціональною мовою (у ній лямбда), то це приклад мови, яка не використовує сміття.
Я мушу сказати, що питання трохи не визначене, оскільки передбачає, що існує стандартна колекція "функціональних мов". Практично кожна мова програмування підтримує деяку кількість функціонального програмування. І майже кожна мова програмування підтримує деяку кількість імперативного програмування. Де можна провести межу, щоб сказати, що є функціональною мовою, а яка - імперативною мовою, крім керованих культурними забобонами та популярною догмою?
Кращим способом сформулювати питання було б: "чи можна підтримувати функціональне програмування у виділеній пам'яті стеку". Відповідь, як уже було сказано, дуже складна. Функціональний стиль програмування сприяє розподілу рекурсивних структур даних за власним бажанням, що вимагає накопичувальної пам’яті (чи збирається сміття, чи відраховується посилання). Однак існує досить складна техніка аналізу компілятора, яка називається аналізом пам'яті на основі регіону, за допомогою якого компілятор може розділити купу на великі блоки, які можуть бути розподілені і розміщені автоматично, таким чином, як розподіл стеків. На сторінці Вікіпедії перераховані різні способи реалізації методики як для "функціональних", так і для "імперативних" мов.