Fable
Fable copied to clipboard
Task vs Promise
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
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
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.
Seems like in .NET there is an exception when you use the constructor to create the task.
Tasks that are created by the public
Taskconstructors are referred to as cold tasks... All other tasks begin their life cycle in a hot state.
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
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?
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
Could we make Giraffe run on Rust?
I guess the first step would be to make the TestTask tests pass.