IO constructor - creates an IO from a function. Automatically detects if the function returns a Promise and handles it appropriately.
Alias for bracket with a more descriptive name.
Runs all IOs in parallel and collects results.
Returns the first effect to succeed, or fails if all fail.
Creates an IO from an async thunk. The Promise is not created until the IO is run.
Creates an IO from an async function that returns { data, error }. Handles both:
This is the most ergonomic way to wrap Supabase and similar API calls.
// Supabase query in one line:
const getUser = (id: string): IO<never, Error, Option<User>> =>
IO.asyncResult(
() => supabase.from('users').select('*').eq('id', id).single(),
toError
)
// With custom field names:
const result = IO.asyncResult(
() => customApi.fetch(),
toError,
{ dataKey: 'result', errorKey: 'err' }
)
// With cancellation:
const controller = new AbortController()
const getUser = IO.asyncResult(
(signal) => supabase.from('users').abortSignal(signal).select('*').single(),
toError,
{ signal: controller.signal }
)
controller.abort() // Cancels the request
Ensures a resource is properly released after use. The release function always runs, even if use fails.
Creates an IO that dies with an unrecoverable defect.
Creates an IO that fails with the given error.
Runs IOs in sequence, returning the first success or last failure.
Executes an effect for each element in the array, collecting results.
Executes effects for each element in parallel (limited concurrency coming later). Alias for forEach.
Creates an IO from an Either.
Converts a nullable value to an IO.
Creates an IO from an Option.
Creates an IO from an Option with custom error.
Creates an IO from a result object with data/error pattern. If error is present (truthy), fails with the error. Otherwise succeeds with Option-wrapped data (None if data is null/undefined).
This handles the common { data, error } response pattern used by
Supabase, many REST APIs, and similar libraries.
const response = { data: user, error: null }
const io = IO.fromResult(response) // IO<never, null, Option<User>> -> Some(user)
const emptyResponse = { data: null, error: null }
const emptyIo = IO.fromResult(emptyResponse) // IO<never, null, Option<User>> -> None
const errorResponse = { data: null, error: new Error("Not found") }
const failedIo = IO.fromResult(errorResponse) // IO<never, Error, Option<null>> -> fails
Creates an IO from a Try.
Creates an IO from a generator function. This enables do-notation style programming.
Creates an IO that is immediately interrupted.
Lifts a Promise-returning function into an IO-returning function.
Lifts a synchronous function into an IO-returning function.
Creates an IO that never completes.
Races multiple effects, returning the first to complete. Note: Other effects are NOT cancelled (JS limitation).
Creates an IO that requires a service identified by the tag. The service must be provided before the effect can be run.
Accesses a service and applies a function to it.
Accesses a service and applies an effectful function to it.
Creates an IO that sleeps for the specified duration.
Creates an IO that succeeds with the given value.
Creates an IO from a synchronous thunk. The function is not executed until the IO is run.
Creates a timeout effect that fails with TimeoutError.
Creates an IO from an async thunk with typed error handling. Catches any thrown errors and maps them using the provided function. Supports cancellation via AbortSignal.
This is a simpler alternative to tryPromise that takes a direct
error mapper function instead of an options object.
const io = IO.tryAsync(
() => fetch('/api/users').then(r => r.json()),
(e) => new ApiError(e)
)
// With cancellation:
const controller = new AbortController()
const io = IO.tryAsync(
(signal) => fetch('/api/users', { signal }).then(r => r.json()),
(e) => new ApiError(e),
controller.signal
)
controller.abort() // Cancels the request
Creates an IO from a function that might throw.
Creates an IO from a Promise with error handling.
Accesses multiple services and applies a function to them. Provides a convenient way to work with multiple dependencies.
Starts a Do-builder context for binding values. This enables do-notation style programming without generators.
Creates a unit IO.
// Basic usage
const program = IO.sync(() => 42)
.map(x => x * 2)
.flatMap(x => IO.succeed(x + 1))
const result = await program.run() // 85
// Error handling
const safe = IO.tryPromise({
try: () => fetch('/api/data'),
catch: (e) => new NetworkError(e)
})
.map(res => res.json())
.recover({ fallback: 'default' })
// Composition
const composed = IO.all([
IO.succeed(1),
IO.succeed(2),
IO.succeed(3)
]) // IO<never, never, [1, 2, 3]>
IO effect type for lazy, composable effects with typed errors.