Подобные функции часто встречаются в комбинаторах с фиксированной запятой. например одна форма Y комбинатора написана λf.(λx.f (x x)) (λx.f (x x))
. Комбинаторы с фиксированной запятой используются для реализации общей рекурсии в нетипизированном лямбда-исчислении без каких-либо дополнительных рекурсивных конструкций, и это является частью того, что делает нетипизированное лямбда-исчисление Тьюринг-полным.
Когда люди разработали лямбда-исчисление простого типа , которое представляет собой наивную систему статического типа поверх лямбда-исчисления, они обнаружили, что больше нельзя писать такие функции. Фактически, невозможно выполнить общую рекурсию в простом типе лямбда-исчисления; и, следовательно, лямбда-исчисление с простым типом больше не является полным по Тьюрингу. (Одним интересным побочным эффектом является то, что программы в простом типе лямбда-исчисления всегда завершаются.)
Настоящие статически типизированные языки программирования, такие как Standard ML, нуждаются во встроенных рекурсивных механизмах для решения проблемы, таких как именованные рекурсивные функции (определенные с помощью val rec
или fun
) и именованные рекурсивные типы данных.
Все еще возможно использовать рекурсивные типы данных для имитации чего-то вроде того, что вы хотите, но это не так красиво.
По сути, вы хотите определить тип как 'a foo = 'a foo -> 'a
; Однако это не допускается. Вместо этого вы оборачиваете его в тип данных: datatype 'a foo = Wrap of ('a foo -> 'a);
datatype 'a foo = Wrap of ('a foo -> 'a);
fun unwrap (Wrap x) = x;
Как правило, Wrap
и unwrap
используются для преобразования между 'a foo
и 'a foo -> 'a
и наоборот.
Всякий раз, когда вам нужно вызвать функцию для себя, вместо x x
, вы должны явно написать (unwrap x) x
(или unwrap x x
); то есть unwrap
превращает его в функцию, которую можно затем применить к исходному значению.
P.S. Другой язык ML, OCaml, имеет возможность включить рекурсивные типы (обычно отключенные); если вы запускаете интерпретатор или компилятор с флагом -rectypes
, то можно написать что-то вроде fun x -> x x
. По сути, за кулисами средство проверки типов определяет места, где необходимо «обернуть» и «развернуть» рекурсивный тип, а затем вставляет их для вас. Я не знаю ни о какой реализации Standard ML, имеющей аналогичную функциональность рекурсивных типов.