Skip to content

Commit

Permalink
Lift a simple function into an operation safely.
Browse files Browse the repository at this point in the history
There is the potential for a function, if it is a continuation, to
cause the scope in which it is called to be destroyed. In this
case, nothing after that function should ever be called. However,
because of the way `lift()` is currently implemented, this will not be
the case.

This unpacks the `lift()` function to be a single instruction that
immediately returns value of invoking the function. However, because
there is a `yield` point in the computation, it is an opportunity to
not continue which will happen if corresponding frame is closed.

At the same time, it removes the named types since they don't really
add much clarity when you look at them from your language server.
  • Loading branch information
cowboyd committed Jan 6, 2024
1 parent 7fa2c1d commit 1f61006
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 11 deletions.
20 changes: 9 additions & 11 deletions lib/lift.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { shift } from "./deps.ts";
import { type Operation } from "./types.ts";

/**
Expand All @@ -16,20 +17,17 @@ import { type Operation } from "./types.ts";
* @returns a function returning an operation that invokes `fn` when evaluated
*/
export function lift<TArgs extends unknown[], TReturn>(
fn: Fn<TArgs, TReturn>,
): LiftedFn<TArgs, TReturn> {
fn: (...args: TArgs) => TReturn,
): (...args: TArgs) => Operation<TReturn> {
return (...args: TArgs) => {
return ({
[Symbol.iterator]() {
let value = fn(...args);
return { next: () => ({ done: true, value }) };
*[Symbol.iterator]() {
return yield () => {
return shift(function* (k) {
k({ ok: true, value: fn(...args) });
});
};
},
});
};
}

type Fn<TArgs extends unknown[], TReturn> = (...args: TArgs) => TReturn;

type LiftedFn<TArgs extends unknown[], TReturn> = (
...args: TArgs
) => Operation<TReturn>;
23 changes: 23 additions & 0 deletions test/lift.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createSignal, each, lift, run, sleep, spawn } from "../mod.ts";
import { describe, expect, it } from "./suite.ts";

describe("lift", () => {
it("safely does not continue if the call stops the operation", async () => {
let reached = false;

await run(function* () {
let signal = createSignal<void, void>();

yield* spawn(function* () {
yield* sleep(0);
yield* lift(signal.close)();

reached = true;
});

for (let _ of yield* each(signal));
});

expect(reached).toBe(false);
});
});

0 comments on commit 1f61006

Please sign in to comment.