---
title: "Standard Option and Result"
description: "The canonical boxed carrier ABI, APIs, propagation rules, and explicit Effect bridge."
image: "https://hem.run/social/hem.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://hem.run/llms.txt
> Use this file to discover all available pages before exploring further.

# Standard Option and Result

Hem ships eager `Option<T>` and `Result<T, E>` values independently of Effect. They live in the
zero-dependency `@hem-lang/std` runtime and are always explicit imports:

```hem
import { Option } from "@hem-lang/std/option"
import { Result } from "@hem-lang/std/result"
```

There is no importable `@hem-lang/std` root. Each focused subpath exports one same-named type/value
pair and no standalone aliases.

## Choose the representation deliberately

| Need | Representation |
| --- | --- |
| Ordinary platform-shaped absence without allocation | `?T` |
| Nested absence, combinators, a stable tag, or must-use signaling | `Option<T>` |
| Expected recoverable failure with a typed payload | `Result<T, E>` |
| Lazy work, interruption, resources, services, or concurrency | `Effect<A, E, R>` |

`Some` allocates a wrapper. That cost is intentional and observable. Use `?T` on hot or
platform-shaped paths where an explicit box provides no advantage.

## Frozen JavaScript ABI

```ts
export type Option<T> =
  | { readonly _tag: "Some"; readonly value: T }
  | { readonly _tag: "None" };

export type Result<T, E> =
  | { readonly _tag: "Ok"; readonly value: T }
  | { readonly _tag: "Err"; readonly error: E };
```

These are transparent, serializable, forgeable tagged objects—not classes or Effect values. Match
on `_tag`; wrapper reference identity is not semantic equality. `Err` names recoverable failure
data. `Error` remains the JavaScript/Hem thrown-error class concept.

Qualified constructors keep the absent branch precise:

```ts
Option.some<A>(value: A): Option<A>
Option.none: Option<never>
Result.ok<A>(value: A): Result<A, never>
Result.err<E>(error: E): Result<never, E>
```

The `never` branch lets TypeScript infer exact error unions through composition.

## Contextual constructors and patterns

Capitalized `Some`/`None` and `Ok`/`Err` are contextual syntax. A checked expected type selects
construction; a checked scrutinee selects matching.

```hem
let port: Option<number> = Some(8080)
let parsed: Result<number, string> = Ok(8080)

let show = (value: Result<number, string>): string => match value {
  | Ok(number) => number.toString()
  | Err(message) => message
}
```

Without an expected carrier, `Some(value)` or `Ok(value)` refuses with HEM2025. Use
`Option.some(value)` or `Result.ok(value)` to establish the type. Recognition follows the resolved
standard declaration, not a type name or structural resemblance.

## Carrier binding

An immutable root `Some` or `Ok` binding with no `else` propagates the original residual to the
nearest arrow, async arrow, or Effect success-channel boundary:

```hem
let decode = (text: string): Result<User, DecodeError> => {
  let Ok(fields) = parseFields(text)
  Result.ok(User.fromFields(fields))
}
```

The RHS evaluates once. Success binds the payload (`.value` on `Ok` and `Some`); failure returns
the original `Err` or `None` object — an `Err` carries its payload as `.error`, so unnarrowed
payload access on a `Result` is a type error. Native `?T` uses the same `Some` spelling and returns the exact observed `null` or
`undefined` sentinel.

The rules are deliberately narrow:

- source and destination use the same carrier representation;
- the nearest arrow, async arrow, or Effect block is the boundary, so a nested callback cannot
  return from its caller;
- only root `Some` and `Ok` propagate—`None` and `Err` never invert success;
- the payload below the carrier root must be statically irrefutable;
- an explicit `else` always selects ordinary `let`–`else` behavior;
- canonical boxed carriers are must-use, while native `?T` is not generally must-use.

Async transport keeps `await` explicit:

```hem
let load = async (): Promise<Result<User, LoadError>> => {
  let Ok(user) = await fetchUser()
  Result.ok(user)
}
```

The Promise resolves before carrier inspection. A resolved residual returns through the async
function; rejection remains rejection. A missing `await`, incompatible destination, nested
refutation, or invalid boundary reports HEM2026 at authored Hem.

## Option API

All operations are synchronous, fixed-arity, uncurried, and data-first.

| Operation | Contract |
| --- | --- |
| `some`, `none` | Construct a present or absent boxed value |
| `isSome`, `isNone` | Narrow by the public tag |
| `map`, `flatMap`, `flatten`, `filter` | Transform or select present values |
| `getOr`, `getOrElse` | Eager or lazy value fallback |
| `orElse` | Lazy alternative Option |
| `fromNullish`, `toNullish` | Explicit boxed/native border; `None` unboxes to `undefined` |
| `toResult` | Convert `None` with a lazy error producer |
| `all` | Combine already-produced values in input order |

## Result API

| Operation | Contract |
| --- | --- |
| `ok`, `err` | Construct success or recoverable failure |
| `isOk`, `isErr` | Narrow by the public tag |
| `map`, `mapError`, `flatMap`, `flatten` | Transform success or error channels |
| `getOr`, `getOrElse` | Eager or lazy success fallback |
| `orElse` | Recover from `Err`, receiving its error payload |
| `toOption` | Deliberately discard the error |
| `all` | Combine already-produced values and union heterogeneous errors |

Callbacks are called only on their documented branch. A callback throw escapes unchanged; the
combinators never turn defects into `Err`. `all([first(), second()])` sees both calls only after
JavaScript has evaluated the array elements. It scans left-to-right and returns the first residual
object, but it does not start, cancel, or schedule work.

The pipe inserts into the first argument, so piped and direct forms have the same call ABI:

```hem
let normalized = option |> Option.map(normalize)
// Option.map(option, normalize)
```

Use an explicit arrow to pipe into a later argument.

## Effect interoperability

Inside a configured `effect` or `resource` block, postfix `!` is the explicit adapter:

```hem
let program = effect {
  let user = lookupResult()!
  let token = lookupOption()!
  render(user, token)
}
```

Canonical `Result<T, E>!` yields `T` or fails with the original `E`. Canonical `Option<T>!` yields
`T` or the configured transport's checked `NoSuchElementError`. Native `?T!` reports HEM2027 because
absence has no error payload or policy. An omitted-`else` carrier binding inside Effect propagates
the unchanged residual through the Effect success channel. Use postfix `!` instead when the
carrier residual should become an Effect failure.

Effect's eager failure carrier is `Result<A, E>`, written `Result.Result<A, E>` through its
namespace; postfix `!` on it emits `Effect.fromResult`. The pre-release bare Effect `Result<T, E>`
alias is removed, so a bare `Result` has one meaning: the canonical carrier.

## Foreign carriers

`better-result`, local variants, and structurally identical TypeScript unions remain ordinary
dependencies. Hem does not vendor them, detect them structurally, or grant them carrier binding or
must-use behavior. Convert explicitly at the integration seam by matching the foreign value and
calling `Option.some`/`none` or `Result.ok`/`err`.

Before granting canonical semantics, the compiler probes the resolved standard declaration
contract. An unsupported package fails once at the project boundary with HEM3006 instead of causing
generated TypeScript errors.

## Related reference

- [Data transformation](/reference/data-transformation) explains first-argument pipes.
- [Absence](/reference/absence) covers native nullish values and exact foreign contracts.
- [Typed failures](/reference/typed-failures) covers the optional Effect failure boundary.

Source: https://hem.run/reference/standard-carriers/index.mdx
