К сожалению, нет тривиального пути.
Нетрудно прочитать исходный код, использовать типы и определить, является ли что-то хвостовым вызовом путем проверки (это «последнее», а не в блоке «try»), но люди вторые. угадай себя и совершай ошибки. Нет простого автоматизированного способа (кроме, например, проверки сгенерированного кода).
Конечно, вы можете просто попробовать свою функцию на большом фрагменте тестовых данных и посмотреть, взорвется он или нет.
Компилятор F # будет генерировать инструкции .tail IL для всех хвостовых вызовов (если только не используются флаги компилятора для их отключения - используется, когда вы хотите сохранить стековые фреймы для отладки), за исключением того, что непосредственно хвостовые рекурсивные функции будет оптимизирован в циклы. (РЕДАКТИРОВАТЬ: я думаю, что в настоящее время компилятор F # также не может генерировать .tail в тех случаях, когда он может доказать, что на этом сайте вызовов нет рекурсивных циклов; это оптимизация, учитывая, что код операции .tail немного медленнее на многих платформах.)
'tailcall' является зарезервированным ключевым словом, с мыслью, что будущая версия F # может позволить вам написать, например,
tailcall func args
, а затем получите предупреждение / ошибку, если это не хвостовой вызов.
Только функции, которые не являются хвостовыми рекурсивами (и, следовательно, нуждаются в дополнительном параметре аккумулятора), «заставят» вас использовать идиому «внутренней функции».
Вот пример кода того, что вы спросили:
let rec nTimes n f x =
if n = 0 then
x
else
nTimes (n-1) f (f x)
let r = nTimes 3 (fun s -> s ^ " is a rose") "A rose"
printfn "%s" r