Можно ли реализовать хвостовую рекурсию с задачами?

Можно ли реализовать хвостовую рекурсию с методами, возвращающими задачи?

В следующем примере не создается правильная функция хвостовой рекурсии:

[<TailCall>]
let rec isThisPossible () =
    task {
        // Do some stuff with tasks here ...
        // Warning: FS3569
        return! isThisPossible()
    }

Когда я заменяю task на async, предупреждение компилятора исчезает, и я могу запустить свой пример без переполнения стека.

Синтаксис вычислительных выражений F# представляет собой синтаксический сахар, который после очистки вызывает методы соответствующего класса-строителя (.Bind, .Return, .ReturnFrom, ...). Таким образом, хвостовые вызовы невозможны, по крайней мере, я так не думаю. На самом деле это может зависеть от реализации вычислительного выражения.

Foxy 29.04.2024 11:23

@Foxy Но async, кажется, работает. Это особая реализация?

Alex H 29.04.2024 13:01

Простой ответ на ваш вопрос: нет, пока нет. Async был реализован более 14 лет назад, и первоклассная поддержка хвостовой рекурсии является неотъемлемой основной функцией. Построитель task был представлен в F# 6, а хвостовая рекурсия пока явно не поддерживается. На различных этапах ведутся языковые дискуссии, охватывающие а) перестройку async для отработки механизмов, лежащих в основе task б) заполнение пробелов в том, что task не может делать хорошо, что async может, например, хвостовая рекурсия, но и другие части

Ruben Bartelink 29.04.2024 13:34

@RubenBartelink Спасибо за ответ. Я извлек данные, связанные с задачей, в функцию и вызвал ее с помощью Async.AwaitTask в функции асинхронной хвостовой рекурсии.

Alex H 29.04.2024 13:42

Да, в принципе, это должно быть достойным обходным путем (плюс-минус Async.AwaitTask, делающий дурацкую упаковку github.com/fsharp/fslang-suggestions/issues/840)

Ruben Bartelink 29.04.2024 15:31
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
5
84
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как обсуждалось в комментариях, здесь возможно решение с рекурсивной асинхронной функцией и отдельной функцией задачи:

let taskFunction param : Task =
    task {
        // Do your task stuff here
        return! Task.CompletedTask
    }

[<TailCall>]
let rec recursiveAsyncFunction param =
    async {
        do! taskFunction param |> Async.AwaitTask
        return! recursiveAsyncFunction param
    }

Другие вопросы по теме