Обзор

When

Branch between two behaviors from a predicate function.

branching

Install

$
Terminal window
pnpm dlx shadcn@latest add https://archyn.net/r/when.json

Copy the files below into the target paths shown in each tab.

ts@lib/flow/when.ts
import { type Awaitable } from "./core/result";
import { type Behavior, type RuntimeContext } from "./core/runtime";
import { choose } from "./choose";
import { task } from "./task";
/**
* Branches between two behaviors using a predicate function.
*
* `when` is a convenience wrapper around `choose` for predicates that can be expressed as ordinary
* synchronous or asynchronous functions.
*
* @typeParam I - Shared input type.
* @typeParam O1 - Yes branch output type.
* @typeParam O2 - No branch output type.
* @typeParam E1 - Yes branch domain error type.
* @typeParam E2 - No branch domain error type.
* @typeParam C - Custom context type.
* @param predicate - Function that chooses the branch.
* @param branches - `yes` and `no` behaviors.
* @returns A behavior producing either branch output and either branch domain error.
*
* @example
* ```ts
* const route = when((user) => user.role === "admin", {
* yes: loadAdminDashboard,
* no: loadUserDashboard,
* });
* ```
*/
export function when<I, O1, O2, E1, E2, C extends object>(
predicate: (input: I, context: RuntimeContext<C>) => Awaitable<boolean>,
branches: {
yes: Behavior<I, O1, E1, C>;
no: Behavior<I, O2, E2, C>;
},
): Behavior<I, O1 | O2, E1 | E2, C> {
const predicateTask = task<I, boolean, never, C>(async (input, context) =>
context.ok(await predicate(input, context)),
);
return choose(predicateTask, branches.yes, branches.no);
}

Update the import paths to match your project setup.

Branch between two Behavior values using a plain predicate function.

Usage

when is a convenience wrapper over choose. It turns a plain predicate callback into a predicate task and then selects the yes or no branch.

Use it when:

  • The branch condition is simple and cannot fail with a domain error.
  • You want less ceremony than creating a predicate Behavior manually.
  • Both branches should receive the same input and runtime context.
import { task } from "@/lib/flow/task";
import { when } from "@/lib/flow/when";
const approve = task((amount: number) => ({ status: "approved" as const, amount }));
const review = task((amount: number) => ({ status: "review" as const, amount }));
const routePayment = when((amount: number) => amount < 1000, { yes: approve, no: review });

Behavior

when creates an internal predicate task and delegates branch execution to choose.

Branch domain errors pass through. Predicate exceptions become runtime errors through the internal task; cancellation follows the same behavior as choose.

Types

Branch outputs and domain errors are unioned.

Import from @/lib/flow/when.

Composition Notes

Use when for ergonomic synchronous or asynchronous branching.

Do not use when when the predicate needs typed domain errors; use choose with a predicate behavior.

Source

ts@lib/flow/when.ts
import { type Awaitable } from "./core/result";
import { type Behavior, type RuntimeContext } from "./core/runtime";
import { choose } from "./choose";
import { task } from "./task";
/**
* Branches between two behaviors using a predicate function.
*
* `when` is a convenience wrapper around `choose` for predicates that can be expressed as ordinary
* synchronous or asynchronous functions.
*
* @typeParam I - Shared input type.
* @typeParam O1 - Yes branch output type.
* @typeParam O2 - No branch output type.
* @typeParam E1 - Yes branch domain error type.
* @typeParam E2 - No branch domain error type.
* @typeParam C - Custom context type.
* @param predicate - Function that chooses the branch.
* @param branches - `yes` and `no` behaviors.
* @returns A behavior producing either branch output and either branch domain error.
*
* @example
* ```ts
* const route = when((user) => user.role === "admin", {
* yes: loadAdminDashboard,
* no: loadUserDashboard,
* });
* ```
*/
export function when<I, O1, O2, E1, E2, C extends object>(
predicate: (input: I, context: RuntimeContext<C>) => Awaitable<boolean>,
branches: {
yes: Behavior<I, O1, E1, C>;
no: Behavior<I, O2, E2, C>;
},
): Behavior<I, O1 | O2, E1 | E2, C> {
const predicateTask = task<I, boolean, never, C>(async (input, context) =>
context.ok(await predicate(input, context)),
);
return choose(predicateTask, branches.yes, branches.no);
}