Overview

Choose

Branch between two behaviors from a behavior predicate.

branching

Install

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

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

ts@lib/flow/choose.ts
import { type Behavior, type RuntimeContext, resultFromSignal, runBehavior } from "./core/runtime";
/**
* Branches between two behaviors using a behavior predicate.
*
* The predicate runs first. If it succeeds with `true`, `onTrue` runs; otherwise `onFalse` runs.
* Predicate, true-branch, and false-branch domain errors are preserved in the return type.
*
* @typeParam I - Shared input type.
* @typeParam O1 - True branch output type.
* @typeParam O2 - False branch output type.
* @typeParam EP - Predicate domain error type.
* @typeParam E1 - True branch domain error type.
* @typeParam E2 - False branch domain error type.
* @typeParam C - Custom context type.
* @param predicate - Behavior that decides which branch to run.
* @param onTrue - Behavior run when the predicate succeeds with `true`.
* @param onFalse - Behavior run when the predicate succeeds with `false`.
* @returns A behavior producing either branch output and every branch domain error.
*
* @example
* ```ts
* const route = choose(isAdmin, loadAdminDashboard, loadUserDashboard);
* ```
*/
export function choose<I, O1, O2, EP, E1, E2, C extends object>(
predicate: Behavior<I, boolean, EP, C>,
onTrue: Behavior<I, O1, E1, C>,
onFalse: Behavior<I, O2, E2, C>,
): Behavior<I, O1 | O2, EP | E1 | E2, C> {
return async (input: I, context: RuntimeContext<C>) => {
if (context.signal.aborted) {
return resultFromSignal(context.signal);
}
const predicateResult = await runBehavior(predicate, input, context);
if (!predicateResult.ok) {
return predicateResult;
}
if (context.signal.aborted) {
return resultFromSignal(context.signal);
}
const branch = predicateResult.value ? onTrue : onFalse;
return runBehavior(branch as Behavior<I, O1 | O2, E1 | E2, C>, input, context);
};
}

Update the import paths to match your project setup.

Branch between two Behavior values using a Behavior predicate.

Usage

choose evaluates a predicate Behavior first, then runs either the true branch or the false branch with the same input and context.

Use it when:

  • The branch decision itself may fail.
  • Branches need the full Flow runtime context.
  • You want predicate, true branch, and false branch errors represented in the returned type.
import { choose } from "@/lib/flow/choose";
import { task } from "@/lib/flow/task";
const isAdmin = task((user: { role: string }) => user.role === "admin");
const adminRoute = task((user: { role: string }) => ({ section: "admin", user }));
const userRoute = task((user: { role: string }) => ({ section: "user", user }));
const routeUser = choose(isAdmin, adminRoute, userRoute);

Behavior

The predicate runs first. Predicate failure returns immediately. Predicate success selects exactly one branch.

Predicate and selected-branch domain errors pass through. Cancellation is checked before predicate and before branch execution.

Types

Predicate and branch errors are unioned, and branch outputs are unioned.

Import from @/lib/flow/choose.

Composition Notes

Use choose when branch selection itself is a behavior that can fail.

Do not use choose when the predicate is a simple function; when is lighter.

Source

ts@lib/flow/choose.ts
import { type Behavior, type RuntimeContext, resultFromSignal, runBehavior } from "./core/runtime";
/**
* Branches between two behaviors using a behavior predicate.
*
* The predicate runs first. If it succeeds with `true`, `onTrue` runs; otherwise `onFalse` runs.
* Predicate, true-branch, and false-branch domain errors are preserved in the return type.
*
* @typeParam I - Shared input type.
* @typeParam O1 - True branch output type.
* @typeParam O2 - False branch output type.
* @typeParam EP - Predicate domain error type.
* @typeParam E1 - True branch domain error type.
* @typeParam E2 - False branch domain error type.
* @typeParam C - Custom context type.
* @param predicate - Behavior that decides which branch to run.
* @param onTrue - Behavior run when the predicate succeeds with `true`.
* @param onFalse - Behavior run when the predicate succeeds with `false`.
* @returns A behavior producing either branch output and every branch domain error.
*
* @example
* ```ts
* const route = choose(isAdmin, loadAdminDashboard, loadUserDashboard);
* ```
*/
export function choose<I, O1, O2, EP, E1, E2, C extends object>(
predicate: Behavior<I, boolean, EP, C>,
onTrue: Behavior<I, O1, E1, C>,
onFalse: Behavior<I, O2, E2, C>,
): Behavior<I, O1 | O2, EP | E1 | E2, C> {
return async (input: I, context: RuntimeContext<C>) => {
if (context.signal.aborted) {
return resultFromSignal(context.signal);
}
const predicateResult = await runBehavior(predicate, input, context);
if (!predicateResult.ok) {
return predicateResult;
}
if (context.signal.aborted) {
return resultFromSignal(context.signal);
}
const branch = predicateResult.value ? onTrue : onFalse;
return runBehavior(branch as Behavior<I, O1 | O2, E1 | E2, C>, input, context);
};
}