Лучший способ выяснить, что происходит, - сказать компилятору вывести его промежуточное представление с -v4
. Вывод объемный и немного сложный для чтения, но он должен позволить вам точно определить, в чем заключается разница в сгенерированном коде и как туда попал компилятор.
Вы, вероятно, заметите, что fA
перемещается за пределы функции (в «глобальную область») в первом примере. В вашем втором примере это, вероятно, не так (имеется в виду, что он будет воссоздан при каждом вызове).
Одной из возможных причин того, что она не была перемещена за пределы функции, может быть то, что компилятор считает, что это зависит от значения n
. В вашем рабочем примере n
не зависит от fA
.
Но я думаю, что причина, по которой компилятор избегает перемещения fA
во втором примере, заключается в том, что он пытается избежать утечки пространства. Подумайте, что произошло бы, если бы fA
вместо вашего массива представлял собой бесконечный список (в котором вы использовали оператор !!
). Представьте, что вы звонили один раз с большим номером (например, f 10000
), а позже звонили только с маленькими номерами (f 2
, f 3
, f 12
...). 10000 элементов из предыдущего вызова все еще находятся в памяти, тратя пространство впустую. Таким образом, чтобы избежать этого, компилятор создает fA
снова каждый раз, когда вы вызываете вашу функцию.
Вероятно, избежание утечки пространства в вашем первом примере не происходит, потому что в этом случае f
фактически вызывается только один раз, возвращая замыкание (сейчас мы находимся на границе чистого функционального и императивного миров, поэтому получить немного более тонкий). Это закрытие заменяет исходную функцию, которая никогда не будет вызываться снова, поэтому fA
вызывается только один раз (и, таким образом, оптимизатор может свободно перемещать ее за пределы функции). Во втором примере f
не заменяется закрытием (так как его значение зависит от аргумента) и, следовательно, будет вызвано снова.
Если вы хотите узнать больше об этом (что поможет прочитать вывод -v4
), вы можете взглянуть на бумагу Spineless Tagless G-Machine (ссылка citeseer ).
Что касается вашего последнего вопроса, я думаю, что это особенность компилятора (но я могу ошибаться). Однако меня не удивит, если все компиляторы будут делать то же самое, даже если это не будет частью языка.