Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions src/Fable.Transforms/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3871,6 +3871,10 @@ let asyncs com (ctx: Context) r t (i: CallInfo) (_: Expr option) (args: Expr lis
Helper.LibCall(com, "Async", "catchAsync", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
|> Some
| "RunSynchronously" -> None
// Task<T> is Promise<T> in JS/TS
| "AwaitTask" ->
Helper.LibCall(com, "Async", "awaitPromise", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
|> Some
// Fable.Core extensions
| meth ->
Helper.LibCall(
Expand All @@ -3885,6 +3889,71 @@ let asyncs com (ctx: Context) r t (i: CallInfo) (_: Expr option) (args: Expr lis
)
|> Some

let taskBuilder (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
match thisArg, i.CompiledName, args with
| _, "Singleton", _ -> makeImportLib com t "singleton" "TaskBuilder" |> Some
| Some x, "Using", [ arg; f ]
| Some x, "TaskBuilderBase.Using", [ arg; f ] ->
Helper.InstanceCall(x, "Using", t, [ arg; f ], i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
|> Some
| Some x, "TaskBuilderBase.Bind", [ arg; f ] ->
Helper.InstanceCall(x, "Bind", t, [ arg; f ], i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
|> Some
| Some x, "TaskBuilderBase.ReturnFrom", [ arg ] ->
Helper.InstanceCall(x, "ReturnFrom", t, [ arg ], i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
|> Some
| Some x, meth, _ ->
Helper.InstanceCall(x, meth, t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
|> Some
| None, meth, _ ->
Helper.LibCall(
com,
"TaskBuilder",
Naming.lowerFirst meth,
t,
args,
i.SignatureArgTypes,
genArgs = i.GenericArgs,
?loc = r
)
|> Some

let tasks (com: ICompiler) (_ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
match thisArg, i.CompiledName with
| Some x, ("GetAwaiter" | "GetResult" | "get_Result" | "Result") ->
// Task<T> = Promise<T>; return the promise - callers use Async.AwaitTask to extract the value
Some x
| None, "FromResult" ->
Helper.LibCall(com, "Task", "fromResult", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| None, ".ctor" ->
Helper.LibCall(
com,
"Task",
"TaskCompletionSource",
t,
args,
i.SignatureArgTypes,
isConstructor = true,
?loc = r
)
|> Some
| Some x, meth ->
Helper.InstanceCall(x, meth, t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
|> Some
| None, meth ->
Helper.LibCall(
com,
"Task",
Naming.lowerFirst meth,
t,
args,
i.SignatureArgTypes,
genArgs = i.GenericArgs,
?loc = r
)
|> Some

let guids
(com: ICompiler)
(ctx: Context)
Expand Down Expand Up @@ -4371,6 +4440,15 @@ let private replacedModules =
"Microsoft.FSharp.Control.AsyncActivation`1", asyncBuilder
"Microsoft.FSharp.Control.FSharpAsync", asyncs
"Microsoft.FSharp.Control.AsyncPrimitives", asyncs
Types.task, tasks
Types.taskGeneric, tasks
"System.Threading.Tasks.TaskCompletionSource`1", tasks
"System.Runtime.CompilerServices.TaskAwaiter`1", tasks
"Microsoft.FSharp.Control.TaskBuilder", taskBuilder
"Microsoft.FSharp.Control.TaskBuilderBase", taskBuilder
"Microsoft.FSharp.Control.TaskBuilderModule", taskBuilder
"Microsoft.FSharp.Control.TaskBuilderExtensions.HighPriority", taskBuilder
"Microsoft.FSharp.Control.TaskBuilderExtensions.LowPriority", taskBuilder
Types.guid, guids
"System.Uri", uris
"System.Lazy`1", laziness
Expand Down
14 changes: 3 additions & 11 deletions src/fable-library-py/fable_library/task_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,9 @@ async def try_with() -> T:
async def Using[T: IDisposable, U](self, resource: T, binder: Callable[[T], Awaitable[U]]) -> U:
return await self.TryFinally(self.Delay(lambda: binder(resource)), lambda: resource.Dispose())

@overload
async def While(self, guard: Callable[[], bool], computation: Delayed[None]) -> None: ...

@overload
async def While[T](self, guard: Callable[[], bool], computation: Delayed[T]) -> T: ...

async def While(self, guard: Callable[[], bool], computation: Delayed[Any]) -> Any:
if guard():
return await self.Bind(computation(), lambda _: self.While(guard, computation))
else:
return await self.Return()
async def While(self, guard: Callable[[], bool], computation: Delayed[None]) -> None:
while guard():
await computation()

async def Zero(self) -> None:
return await zero()
Expand Down
37 changes: 37 additions & 0 deletions src/fable-library-ts/Task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { OperationCanceledException } from "./AsyncBuilder.ts";

export class TaskCompletionSource<T> {
private _resolve!: (value: T) => void;
private _reject!: (reason?: unknown) => void;
public task: Promise<T>;

constructor() {
this.task = new Promise<T>((resolve, reject) => {
this._resolve = resolve;
this._reject = reject;
});
}

SetResult(value: T): void { this._resolve(value); }
SetException(error: unknown): void { this._reject(error); }
SetCancelled(): void { this._reject(new OperationCanceledException()); }
get_Task(): Promise<T> { return this.task; }
}

export function fromResult<T>(value: T): Promise<T> {
return Promise.resolve(value);
}

export function zero(): Promise<void> {
return Promise.resolve();
}

// Task<T> = Promise<T> in JS/TS. GetAwaiter/GetResult return the Promise itself;
// callers should use Async.AwaitTask to extract the value in an async context.
export function getAwaiter<T>(t: Promise<T>): Promise<T> {
return t;
}

export function getResult<T>(t: Promise<T>): Promise<T> {
return t;
}
85 changes: 85 additions & 0 deletions src/fable-library-ts/TaskBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { IDisposable } from "./Util.ts";
import { zero } from "./Task.ts";

export class TaskBuilder {
public Bind<T, U>(computation: Promise<T>, binder: (x: T) => Promise<U>): Promise<U> {
return computation.then(binder);
}

public Combine<T>(computation1: Promise<void>, computation2: () => Promise<T>): Promise<T> {
return computation1.then(computation2);
}

public Delay<T>(generator: () => Promise<T>): () => Promise<T> {
return generator;
}

public For<T>(sequence: Iterable<T>, body: (x: T) => Promise<void>): Promise<void> {
const iter = sequence[Symbol.iterator]();
let cur = iter.next();
return this.While(
() => !cur.done,
this.Delay(() => {
const res = body(cur.value!);
cur = iter.next();
return res;
})
);
}

public Return<T>(value?: T): Promise<T | undefined> {
return Promise.resolve(value);
}

public ReturnFrom<T>(computation: Promise<T>): Promise<T> {
return computation;
}

public TryFinally<T>(computation: () => Promise<T>, compensation: () => void): Promise<T> {
try {
return computation().finally(compensation);
} catch (e) {
compensation();
return Promise.reject(e);
}
}

public TryWith<T>(computation: () => Promise<T>, catchHandler: (e: unknown) => Promise<T>): Promise<T> {
try {
return computation().catch(catchHandler);
} catch (e) {
try {
return catchHandler(e);
} catch (e2) {
return Promise.reject(e2);
}
}
}

public Using<T extends IDisposable, U>(resource: T, binder: (x: T) => Promise<U>): Promise<U> {
return this.TryFinally(() => binder(resource), () => resource.Dispose());
}

public While(guard: () => boolean, computation: () => Promise<void>): Promise<void> {
return (async () => {
while (guard()) {
await computation();
}
})();
}

public Zero(): Promise<void> {
return zero();
}

public Run<T>(computation: () => Promise<T>): Promise<T> {
try {
return computation();
} catch (e) {
return Promise.reject(e);
}
}
}

export const singleton = new TaskBuilder();
export function task(): TaskBuilder { return singleton; }
1 change: 1 addition & 0 deletions tests/Js/Main/Fable.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
<Compile Include="RandomTests.fs" />
<Compile Include="ArrayTests.fs" />
<Compile Include="AsyncTests.fs" />
<Compile Include="TaskTests.fs" />
<Compile Include="CharTests.fs" />
<Compile Include="ComparisonTests.fs" />
<Compile Include="ConditionalWeakTableTests.fs" />
Expand Down
1 change: 1 addition & 0 deletions tests/Js/Main/Main.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ let allTests =
RandomTests.tests
Arrays.tests
Async.tests
Task.tests
Chars.tests
Comparison.tests
ConditionalWeakTable.tests
Expand Down
Loading
Loading