Чому функція "нічого не робить" Haskell, id, споживає багато пам'яті?


112

Haskell має функцію ідентичності, яка повертає вхід незмінним. Визначення просте:

id :: a -> a
id x = x

Тому для задоволення це повинно вивести 8:

f = id id id id id id id id id id id id id id id id id id id id id id id id id id id
main = print $ f 8

Через декілька секунд (і приблизно 2 Гб пам'яті відповідно до диспетчера завдань) компіляція не вдається ghc: out of memory. Аналогічно говорить перекладач ghci: out of memory.

Оскільки idце досить проста функція, я б не очікував, що це буде обтяження пам'яті під час виконання або час компіляції. Для чого використовується вся пам'ять?


11
Ви хочете скласти ці id. У VIM, з курсором на визначення f, зробіть наступне: :s/id id/id . id ./g.
Тобіас Брандт,

Відповіді:


135

Ми знаємо тип id,

id :: a -> a

І коли ми спеціалізуємося на цьому id id, ліва копія idмає тип:

id :: (a -> a) -> (a -> a)

І тоді , коли ви спеціалізуєтесь це знову крайній лівий idв id id id, ви отримаєте:

id :: ((a -> a) -> (a -> a)) -> ((a -> a) -> (a -> a))

Отже, ви бачите кожен доданий idвами підпис типу крайньої лівої частини idвдвічі більший.

Зауважте, що типи видаляються під час компіляції, тому це займе лише пам'ять у GHC. Він не займе пам'ять у вашій програмі.


Я пам’ятаю, що Окасакі зіткнувся з якоюсь подібною проблемою, коли написав калькулятор RPN, вбудований у Haskell.
dfeuer

3
Можливо, питання полягає в тому, чи повинен GHC знайти спосіб вирішити подібні речі більш витончено. Зокрема, тип дуже великий, якщо виписаний у повному обсязі, але є величезна кількість дублювання - чи можна спільний доступ використовувати для стискання таких речей? Чи є ефективний спосіб їх обробки?
dfeuer

5
@dfeuer Спробуйте запитати тип у ghci. Швидкістю відповіді ви побачите, що вона повинна здійснювати відповідний обмін. Я підозрюю, що цей обмін втрачено - з очевидних причин - як тільки ви переведете на якесь інше проміжне представництво (наприклад, ядро).
Даніель Вагнер

4
Це означає, що якщо idповторюється nраз, простір його типу пропорційний 2^n. Типі 2^27, що робиться в коді Райана, знадобляться посилання на його змінну типу на додаток до іншої структури, необхідної для представлення типу, яка, ймовірно, набагато більша, ніж ви очікували, що це буде більшість типів.
Девід

58
Виконання виводу типу наївно є подвійним експоненціальним, вміло використовуючи спільне використання у виразах типу, ви можете звести його до просто експоненціального. Але незалежно від того, що ви робите, з’являться кілька досить простих виразів, які змусять перевірити тип вибуху. На щастя, це не відбувається в практичному програмуванні.
серпень
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.