Почему встроенная функция применяется к слишком малым аргументам, которые считаются в слабой форме головы?

Определение Haskell говорит:

Выражение в слабой форме головы (WHNF), если оно равно:

  • конструктор (в конечном итоге применяется к аргументам), например True, Just (квадрат 42) или (:) 1
  • встроенная функция применяется к слишком маленьким аргументам (возможно, к ней не относится), например (+) 2 или sqrt.
  • или выражение lambda abstraction\x → .

Почему встроенные функции получают специальное лечение? Согласно исчислению лямбда, нет никакой разницы между частично примененной функцией и любой другой функцией, потому что в конце мы имеем только одну функцию аргумента.

Ответы

Ответ 1

Нормальная функция, применяемая к аргументу, такая как:

(\x y -> x + 1 : y) 1

Можно уменьшить, чтобы дать:

\y -> 1 + 1 : y

В первом выражении "внешняя" вещь была приложением, поэтому она не была в WHNF. Во втором случае самая внешняя вещь - абстракция лямбда, поэтому она находится в WHNF (хотя мы могли бы сделать больше сокращений внутри тела функции).

Теперь рассмотрим применение встроенной (примитивной) функции:

(+) 1

Поскольку это встроенный, нет тела функции, в котором мы можем заменить 1 на первый параметр. Оценщик "просто знает", как оценить полностью "насыщенные" приложения (+), например (+) 1 2. Но ничего, что можно сделать с помощью частично встроенного встроенного; все, что мы можем сделать, это создать структуру данных, описывающую "применять (+) до 1 и ждать еще один аргумент", но именно то, что мы пытаемся уменьшить ,. Поэтому мы ничего не делаем.

Встроенные функции являются особенными, поскольку они не определяются выражениями лямбда-исчисления, поэтому процесс сокращения не может "видеть внутри" их определение. Таким образом, в отличие от приложений с обычными функциями, встроенные функциональные приложения должны быть "уменьшены" путем накопления аргументов, пока они не будут полностью "насыщены" (в этом случае сокращение до WHNF осуществляется путем выполнения любой магической реализации встроенного), Ненасыщенные встроенные приложения больше не могут быть уменьшены, и они уже находятся в WHNF.

Ответ 2

Рассмотрим

Prelude > let f n = [(+ x) | x < - [1..]]!! п
Prelude > let g = f 20000000:: Int → Int

g находится в этой точке не в WHNF! Вы можете увидеть это, оценив, скажем, g 3, что требует заметного отставания, потому что вам нужно WHNF, прежде чем вы сможете применить аргумент. Это когда список перемещается в поисках правильной встроенной функции. Но впоследствии этот выбор фиксируется, g является WHNF (и, действительно, NF: это то же самое для лямбда, возможно, то, что вы имели в виду с вашим вопросом), и, следовательно, любые последующие вызовы немедленно дадут результат.