Fable icon indicating copy to clipboard operation
Fable copied to clipboard

Task vs Promise

Open dbrattli opened this issue 1 year ago • 6 comments
trafficstars

Description

It's currently hard to use Fable to write code that uses Task and compile to both Python and JS/TS. Python translates Task to native Task/Future awaitables (async / await) while for TS/JS you need to use JS.Promise.

This makes the code base filled with #if/#else even if you try to rename Promise to Task. Is it time to support Task in Fable for JS/TS that the task CE is native for F#? Ref: https://github.com/dbrattli/Fable.Giraffe/issues/13#issuecomment-1268608502

dbrattli avatar Dec 18 '23 08:12 dbrattli

Are Task in .NET hot or cold? In the sense do they execute on creation or wait to be invoked?

Could it lead to problems if Task are cold in .NET and/or Python, while in JavaScript promise are hot ?

While we decide on what to do here, a possible workaround for people who need to add cross platform asynchronous code is to create a module which encapsulate the compiler directives. Could benefit from helpers functions too.

Note: Code below use async but I believe the same could be applied to Task

module Main

open Fable.Core

// We need the `<'T>` here as it allows to mark the function/variable inline for better output.
let inline crossAsync<'T> =
#if FABLE_COMPILER
    promise
#else
    async
#endif

type CrossAsync<'T> =
#if FABLE_COMPILER
    JS.Promise<'T>
#else
    Async<'T>
#endif

let hello (name : string) : CrossAsync<string> =
    crossAsync {
        return "Hello " + name
    }

// Fake the API being called by the consumer code
crossAsync {
    let! hello = hello "World"
    printfn "%s" hello
    return ()
}
#if FABLE_COMPILER
|> Promise.start
#else
|> Async.Start
#endif

MangelMaxime avatar Dec 18 '23 08:12 MangelMaxime

Tasks in .NET are hot in the sense that when you hold a Task returned from an async method then it's already running regardless if you use await or not. So Task in .NET and Promises in JS are both hot. Tasks in Python are scheduled Futures so they are hot. This is also why I'm unsure about rewriting all Task based code to use F# async for interoperability.

The workaround is nice if you own the codebase but e.g for Fable.Giraffe I want users to use their existing Giraffe webapp without having to modify their code. I.e they can decide if they want to run their web server on .NET, Python or Node (or a combination). Then it would be nice if we could call a Task for a Task.

dbrattli avatar Dec 18 '23 10:12 dbrattli

Seems like in .NET there is an exception when you use the constructor to create the task.

Tasks that are created by the public Task constructors are referred to as cold tasks... All other tasks begin their life cycle in a hot state.

Source

But this is something we can probably ignore (will need some testing so see how often this "problem" occurs).

If I am reading you correctly, then we have:

  • .NET task are hot
  • Python scheduled futures are hot
  • JavaScript promise are hot
  • Rust ? @ncave What are your plans for supporting Task in Rust? Does Rust have hot task?

If all the these major targets have the same hot behaviour available natively, I think it could makes sense to try implements an equivalence between them. This would allows, people to share the same code but also to use the native APIs.

Currently, as you mentioned in your first message, we have these situations:

Situation 1

People prefers to use native primitives and so use Promise for JavaScript and Async for .Net.

Then if they need to merge the code, they needs to use compilers directives or helpers like Async.AwaitPromise / Async.StartAsPromise

Situation 2

People value more shared code and write everything using Async which are natives to .NET but not to JavaScript for example.

So implementing Task by making them use the native APIs would create a third situation which is the best of both world:

Ability to use native F# API which is standard in F# so shareable and use native APIs in the runtime.

MangelMaxime avatar Dec 18 '23 10:12 MangelMaxime

@MangelMaxime

What are your plans for supporting Task in Rust?

There is already some partial support for Async/AsyncBuilder, Task/TaskBuilder, Thread/ThreadPool/Monitor added by @alexswan10k.

Does Rust have hot task?

Afaik Rust's futures are cold, but I'm not sure that is a showstopper.

@dbrattli

It's currently hard to use Fable to write code that uses Task

Can you please elaborate, just trying to understand what the stated goal is. Is it to get most of the languages (JS/TS/Rust/Python) to support at least what is needed to pass the TestTask tests?

ncave avatar Dec 18 '23 16:12 ncave

Yes, started porting Fable.Giraffe to TS/JS. It already runs on Python so the current Task tests would solve my needs. Could we make Giraffe run on Rust? 🙈

dbrattli avatar Dec 18 '23 17:12 dbrattli

@dbrattli

Could we make Giraffe run on Rust?

I guess the first step would be to make the TestTask tests pass.

ncave avatar Dec 18 '23 17:12 ncave