From 3180222d20ef5f00e12bc71e06d63118d720ac35 Mon Sep 17 00:00:00 2001 From: Jacob Bolda Date: Mon, 21 Apr 2025 17:21:37 -0500 Subject: [PATCH 1/2] convert fx to effection v4 --- fx/parallel.test.ts | 19 +++++++++++++++---- fx/parallel.ts | 19 +++++++++++-------- fx/race.test.ts | 4 +++- fx/race.ts | 43 +++++++++++++++++++++++++------------------ fx/request.test.ts | 2 +- fx/request.ts | 10 +++++++--- fx/safe.test.ts | 2 +- fx/safe.ts | 11 ++++++----- fx/type.ts | 5 ----- 9 files changed, 69 insertions(+), 46 deletions(-) delete mode 100644 fx/type.ts diff --git a/fx/parallel.test.ts b/fx/parallel.test.ts index 9645eb79..027c2c6e 100644 --- a/fx/parallel.test.ts +++ b/fx/parallel.test.ts @@ -1,10 +1,18 @@ import { describe, it } from "bdd"; import { expect } from "expect"; -import { each, Err, Ok, run, sleep, spawn } from "npm:effection@3.0.3"; +import { + each, + Err, + Ok, + run, + sleep, + spawn, + until, +} from "npm:effection@4.0.0-alpha.8"; import { parallel } from "./parallel.ts"; -import type { Operation, Result } from "npm:effection@3.0.3"; +import type { Operation, Result } from "npm:effection@4.0.0-alpha.8"; const test = describe("parallel()"); @@ -130,7 +138,7 @@ it(test, "should resolve all async items", async () => { yield* sleep(15); two.resolve(2); }); - const results = yield* parallel([one, () => two.promise]); + const results = yield* parallel([one, () => until(two.promise)]); return yield* results; }); @@ -144,7 +152,10 @@ it(test, "should stop all operations when there is an error", async () => { function* genFn() { try { - const results = yield* parallel([() => one.promise, () => two.promise]); + const results = yield* parallel([ + () => until(one.promise), + () => until(two.promise), + ]); actual = yield* results; } catch (_) { actual = [Err(new Error("should not get hit"))]; diff --git a/fx/parallel.ts b/fx/parallel.ts index 39755f66..c431e0eb 100644 --- a/fx/parallel.ts +++ b/fx/parallel.ts @@ -1,10 +1,14 @@ -import type { Callable, Channel, Operation, Result } from "npm:effection@3.0.3"; -import { createChannel, resource, spawn } from "npm:effection@3.0.3"; +import type { + Channel, + Operation, + Result, + Task, +} from "npm:effection@4.0.0-alpha.8"; +import { createChannel, resource, spawn } from "npm:effection@4.0.0-alpha.8"; import { safe } from "./safe.ts"; -import type { Computation } from "./type.ts"; -export interface ParallelRet extends Computation[]> { +export interface ParallelRet extends Operation[]> { sequence: Channel, void>; immediate: Channel, void>; } @@ -61,8 +65,8 @@ export interface ParallelRet extends Computation[]> { * } * ``` */ -export function parallel( - operations: Callable[], +export function parallel( + operations: ((...args: TArgs) => Operation)[], ): Operation> { const sequence = createChannel>(); const immediate = createChannel>(); @@ -70,7 +74,7 @@ export function parallel( return resource>(function* (provide) { const task = yield* spawn(function* () { - const tasks = []; + const tasks = [] as Task>[]; for (const op of operations) { tasks.push( yield* spawn(function* () { @@ -95,7 +99,6 @@ export function parallel( yield* task; return results; } - yield* provide({ sequence, immediate, diff --git a/fx/race.test.ts b/fx/race.test.ts index 7cf25385..b9196947 100644 --- a/fx/race.test.ts +++ b/fx/race.test.ts @@ -1,6 +1,6 @@ import { describe, it } from "bdd"; import { expect } from "expect"; -import { run, sleep } from "npm:effection@3.0.3"; +import { run, sleep } from "npm:effection@4.0.0-alpha.8"; import { raceMap } from "./race.ts"; @@ -10,6 +10,7 @@ it( test, "should return the result of the first completed operation", async () => { + expect.assertions(1); let winner; const result = await run(function* () { @@ -34,6 +35,7 @@ it( ); it(test, "should halt other operations when one completes", async () => { + expect.assertions(1); let winner; let secondCompleted = false; diff --git a/fx/race.ts b/fx/race.ts index 4f893516..c679d8dc 100644 --- a/fx/race.ts +++ b/fx/race.ts @@ -1,9 +1,9 @@ // deno-lint-ignore-file no-explicit-any -import type { Callable, Operation, Task } from "npm:effection@3.0.3"; -import { action, call, resource, spawn } from "npm:effection@3.0.3"; +import type { Operation, Task } from "npm:effection@4.0.0-alpha.8"; +import { resource, spawn, withResolvers } from "npm:effection@4.0.0-alpha.8"; -interface OpMap { - [key: string]: Callable; +interface OpMap { + [key: string]: (...args: TArgs) => Operation; } export function raceMap(opMap: OpMap): Operation< @@ -16,21 +16,28 @@ export function raceMap(opMap: OpMap): Operation< return resource(function* Race(provide) { const keys = Object.keys(opMap); const taskMap: { [key: string]: Task } = {}; - const resultMap: { [key: keyof OpMap]: OpMap[keyof OpMap] } = {}; - - const winner = yield* action>(function* (resolve) { - for (let i = 0; i < keys.length; i += 1) { - const key = keys[i]; - yield* spawn(function* () { - const task = yield* spawn(function* () { - yield* call(opMap[key] as any); + const resultMap: { [key: keyof OpMap]: ReturnType } = + {}; + + function* start() { + const resolvers = withResolvers(); + + yield* spawn(function* () { + for (let i = 0; i < keys.length; i += 1) { + const key = keys[i]; + yield* spawn(function* () { + const task = yield* spawn(opMap[key]); + taskMap[key] = task; + (resultMap[key] as any) = yield* task; + resolvers.resolve(task); }); - taskMap[key] = task; - (resultMap as any)[key] = yield* task; - resolve(task); - }); - } - }); + } + }); + + return yield* resolvers.operation; + } + + const winner = yield* start(); for (let i = 0; i < keys.length; i += 1) { const key = keys[i]; diff --git a/fx/request.test.ts b/fx/request.test.ts index 90f34a72..0f780564 100644 --- a/fx/request.test.ts +++ b/fx/request.test.ts @@ -1,6 +1,6 @@ import { describe, it } from "bdd"; import { expect } from "expect"; -import { run } from "npm:effection@3.0.3"; +import { run } from "npm:effection@4.0.0-alpha.8"; import { json, request } from "./request.ts"; diff --git a/fx/request.ts b/fx/request.ts index 2f56d0cd..0d4536d8 100644 --- a/fx/request.ts +++ b/fx/request.ts @@ -1,14 +1,18 @@ -import { call, type Operation, useAbortSignal } from "npm:effection@3.0.3"; +import { + type Operation, + until, + useAbortSignal, +} from "npm:effection@4.0.0-alpha.8"; export function* request( url: string | URL | Request, opts?: RequestInit, ): Operation { const signal = yield* useAbortSignal(); - const response = yield* call(() => fetch(url, { signal, ...opts })); + const response = yield* until(fetch(url, { signal, ...opts })); return response; } export function* json(response: Response): Operation { - return yield* call(() => response.json()); + return yield* until(response.json()); } diff --git a/fx/safe.test.ts b/fx/safe.test.ts index d701a8e0..2d751ea7 100644 --- a/fx/safe.test.ts +++ b/fx/safe.test.ts @@ -1,6 +1,6 @@ import { describe, it } from "bdd"; import { expect } from "expect"; -import { call, run } from "npm:effection@3.0.3"; +import { call, run } from "npm:effection@4.0.0-alpha.8"; const tests = describe("call()"); diff --git a/fx/safe.ts b/fx/safe.ts index dd008ad0..1013f5da 100644 --- a/fx/safe.ts +++ b/fx/safe.ts @@ -1,5 +1,5 @@ -import type { Callable, Operation, Result } from "npm:effection@3.0.3"; -import { call, Err, Ok } from "npm:effection@3.0.3"; +import type { Operation, Result } from "npm:effection@4.0.0-alpha.8"; +import { call, Err, Ok } from "npm:effection@4.0.0-alpha.8"; /** * The goal of `safe` is to wrap Operations to prevent them from raising @@ -23,10 +23,11 @@ function isError(error: unknown): error is Error { return error instanceof Error; } -export function* safe(operator: Callable): Operation> { +export function* safe( + operator: (...args: TArgs) => Operation, +): Operation> { try { - // deno-lint-ignore no-explicit-any - const value = yield* call(operator as any); + const value = yield* call(operator); return Ok(value); } catch (error) { return Err(isError(error) ? error : new Error(String(error))); diff --git a/fx/type.ts b/fx/type.ts deleted file mode 100644 index 9bfa49a7..00000000 --- a/fx/type.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { Instruction } from "npm:effection@3.0.3"; -export interface Computation { - // deno-lint-ignore no-explicit-any - [Symbol.iterator](): Iterator; -} From 237b442c4f0316c99c830daf78c9aa8a4a5baa80 Mon Sep 17 00:00:00 2001 From: Jacob Bolda Date: Mon, 21 Apr 2025 20:55:45 -0500 Subject: [PATCH 2/2] remove outer spawn, not required --- fx/race.test.ts | 2 -- fx/race.ts | 20 +++++++++----------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/fx/race.test.ts b/fx/race.test.ts index b9196947..70404d53 100644 --- a/fx/race.test.ts +++ b/fx/race.test.ts @@ -10,7 +10,6 @@ it( test, "should return the result of the first completed operation", async () => { - expect.assertions(1); let winner; const result = await run(function* () { @@ -35,7 +34,6 @@ it( ); it(test, "should halt other operations when one completes", async () => { - expect.assertions(1); let winner; let secondCompleted = false; diff --git a/fx/race.ts b/fx/race.ts index c679d8dc..1354a1fa 100644 --- a/fx/race.ts +++ b/fx/race.ts @@ -22,17 +22,15 @@ export function raceMap(opMap: OpMap): Operation< function* start() { const resolvers = withResolvers(); - yield* spawn(function* () { - for (let i = 0; i < keys.length; i += 1) { - const key = keys[i]; - yield* spawn(function* () { - const task = yield* spawn(opMap[key]); - taskMap[key] = task; - (resultMap[key] as any) = yield* task; - resolvers.resolve(task); - }); - } - }); + for (let i = 0; i < keys.length; i += 1) { + const key = keys[i]; + yield* spawn(function* () { + const task = yield* spawn(opMap[key]); + taskMap[key] = task; + (resultMap[key] as any) = yield* task; + resolvers.resolve(task); + }); + } return yield* resolvers.operation; }