Claude
Skills
Sign in
Back

oxcaml

Included with Lifetime
$97 forever

Working with the OxCaml extensions to OCaml. Use when the oxcaml compiler is available and you need high-performance, unboxing, stack allocation, data-race-free parallelism

General

What this skill does


You are writing code for the OxCaml compiler, a performance-focused fork of
OCaml with Jane Street extensions. This guide covers OxCaml-specific features.
You should already know standard OCaml.

**Current target**: OxCaml `5.2.0minus-31`. The OCaml runtime reports
itself as `5.2.0+ox` (unchanged from `5.2.0minus-25` — the runtime base
has not moved). What did change in this window is the **bootstrap
toolchain requirement**: building OxCaml now needs upstream OCaml
`5.4.0` on `PATH` (previously `4.14.1`), via the `oxcaml-dev` opam
package. A handful of 5.4.0 stdlib features (notably `Format_doc`, the
`caml_array_make` symbol rename) were backported.

See [CHANGES-25-31.md](CHANGES-25-31.md) for what changed since
`5.2.0minus-25`, and [CHANGES-23-25.md](CHANGES-23-25.md) for the prior
window. The upgrade guide at the bottom of CHANGES-25-31.md is the
right starting point when bumping a project between these versions.

### Quick Upgrade Flags (25 → 31)

When diagnosing build failures after a version bump, check these first:

- **Bootstrap compiler** — building OxCaml now needs upstream OCaml `5.4.0`
  on `PATH` (was `4.14.1`). The OxCaml runtime itself is still `5.2.0+ox`
  — this is only the dev-time requirement. Use the `oxcaml-dev` opam
  package per `HACKING.md`.
- **Reserved keywords** — `borrow_`, `poly_`, `repr_`, `kind_`, plus literals
  `#true`, `#false`, `#()` are now reserved. Identifiers collide → rename.
- **`kind_abbrev_` → `kind_`** — the keyword was renamed. Mechanical fix.
- **Mixed block layout v4 → v5** — `mixed_block_layout_v4` gone; use
  `mixed_block_layout_v5`. C macro renames to match.
- **`Effect` low-level primitives refactored** — public `('a, 'b)
  continuation` is unchanged; internal `cont` gained a termination type
  parameter and low-level primitives were renamed. Migrate
  `caml_alloc_stack`/`%runstack` → `%with_stack`, and update `%resume`
  call sites.
- **Array primitive bindings** — `Array.make` / `Array.create_float` now
  bind to `caml_array_make` / `caml_array_create_float`. Re-promote any
  zero-alloc-checker expect tests. The old C symbols remain as shims,
  so C externals linking against them still work.
- **Non-`value` base kinds imply `mod external_`** — if you need the unmoded
  form, use `_internal` (e.g. `bits64_internal`).
- **`Obj.raw_field` / `Obj.set_raw_field` no longer `external`** — they're
  now regular `val`s.

See CHANGES-25-31.md § *Breaking Changes and Upgrade Guide* for the full list
and an ordered checklist.

## Detailed Guides

For in-depth coverage of each feature, see:

| Feature | Guide |
|---------|-------|
| **Modes** (local, unique, once, portable, contended) | [SKILL-MODES.md](SKILL-MODES.md) |
| **Stack Allocation** (local_, stack_, exclave_) | [SKILL-STACK-ALLOCATION.md](SKILL-STACK-ALLOCATION.md) |
| **Unboxed Types** (float#, int32#, mixed blocks) | [SKILL-UNBOXED.md](SKILL-UNBOXED.md) |
| **Kinds** (value, float64, bits32, kind products) | [SKILL-KINDS.md](SKILL-KINDS.md) |
| **Uniqueness** (unique/aliased, once/many) | [SKILL-UNIQUENESS.md](SKILL-UNIQUENESS.md) |
| **Comprehensions** (list/array builders) | [SKILL-COMPREHENSIONS.md](SKILL-COMPREHENSIONS.md) |
| **SIMD** (vector types, SSE/AVX intrinsics) | [SKILL-SIMD.md](SKILL-SIMD.md) |
| **Templates** (ppx_template, mangling) | [SKILL-TEMPLATES.md](SKILL-TEMPLATES.md) |
| **Zero-Alloc** ([@zero_alloc] checking) | [SKILL-ZERO-ALLOC.md](SKILL-ZERO-ALLOC.md) |
| **Base Library** (OxCaml extensions) | [SKILL-BASE.md](SKILL-BASE.md) |
| **Core Library** (OxCaml extensions) | [SKILL-CORE.md](SKILL-CORE.md) |

---

## Quick Reference: Syntax Cheat Sheet

```ocaml
(* Stack allocation *)
let f () = exclave_ stack_ (1, 2)      (* allocate on stack, return local *)
let g (x @ local) = ...                 (* local parameter *)

(* Unboxed types *)
let x : float# = #3.14                  (* unboxed float *)
let y : int32# = #42l                   (* unboxed int32 *)
let b : bool# = #true                   (* unboxed bool (5.2.0minus-31+) *)
let u : unit# = #()                     (* unboxed unit (5.2.0minus-31+) *)
type t = { a : int; b : float# }        (* mixed block record *)

(* Modes on values *)
let f (x @ local unique once) = ...     (* multiple modes *)
val g : t @ global -> t @ local         (* in signatures *)

(* Kinds on types *)
type ('a : float64) t = ...             (* kind annotation *)
val f : ('a : value). 'a -> 'a          (* kind-polymorphic *)

(* Comprehensions *)
[ x * 2 for x = 1 to 10 when x mod 2 = 0 ]
[| y for y in arr when y > 0 |]

(* Labeled tuples *)
let pair = ~x:1, ~y:2                   (* labeled tuple *)
let ~x, ~y = pair                       (* destructuring *)

(* Immutable arrays *)
let arr : int iarray = [: 1; 2; 3 :]
let x = arr.:(0)

(* Unboxed tuple destructuring - use #(...) pattern *)
let #(a, b) = some_unboxed_pair
let #(x, y, z) = fork_join3 par f1 f2 f3

(* Zero-alloc annotation *)
let[@zero_alloc] fast_add x y = x + y

(* Borrowing (5.2.0minus-31+) - prefix form cooperating with uniqueness *)
let r = ref 0 in f (borrow_ r)          (* typical: function argument *)

(* Implicit kinds in signatures (5.2.0minus-31+) *)
module type S = sig
  [@@@implicit_kind: ('elt : word)]
  type 'elt collection                  (* 'elt defaults to kind word *)
end
```

---

## 1. Modes

Modes track runtime properties of values. Each mode axis is independent.

### Mode Axes

| Axis | Values | Default | Purpose |
|------|--------|---------|---------|
| Locality | `local`, `global` | `global` | Where value lives (stack vs heap) |
| Uniqueness | `unique`, `aliased` | `aliased` | Number of references |
| Linearity | `once`, `many` | `many` | How often closures can be called |
| Portability | `portable`, `shareable`, `nonportable` | `nonportable` | Cross-thread safety |
| Contention | `contended`, `shared`, `uncontended` | `uncontended` | Thread access patterns |

### Syntax

```ocaml
(* On parameters *)
let f (x @ local) = ...
let f (x @ local unique) = ...       (* multiple modes *)

(* On return types in signatures *)
val f : t @ local -> t @ global
val g : t @ unique once -> t @ aliased many

(* On expressions *)
let x = (expr : t @ local)

(* On let bindings *)
let local_ x = ...                    (* shorthand for local *)
let global_ x = ...

(* On record fields - modalities *)
type t = {
  global_ data : int;                 (* always global *)
  mutable x : int @@ aliased;         (* aliased modality *)
}
```

### Subtyping Rules

More restrictive modes can be used where less restrictive are expected:
- `local` ≤ `global` (can use local where global expected? NO - reversed)
- `global` ≤ `local` (can use global where local expected)
- `unique` ≤ `aliased` (can use unique where aliased expected)
- `many` ≤ `once` (can use many where once expected)
- `portable` ≤ `shareable` ≤ `nonportable`
- `uncontended` ≤ `shared` ≤ `contended`

---

## 2. Stack Allocation (Locality)

Stack-allocated values avoid GC overhead but cannot escape their scope.

### Key Constructs

```ocaml
(* Allocate on stack *)
let f () =
  let local_ x = (1, 2) in            (* stack-allocated tuple *)
  ...

(* Force stack allocation *)
let f () =
  stack_ (1, 2)                        (* explicitly stack-allocate *)

(* Return local value from function *)
let f () = exclave_
  stack_ (1, 2)                        (* return value allocated in caller's frame *)

(* Combined pattern for local returns *)
let f () = exclave_ stack_ (make_tuple ())
```

### Rules

1. Local values CANNOT escape their defining scope (no storing in globals, no returning without `exclave_`)
2. Local values CAN reference global values
3. Global values CANNOT reference local values
4. `exclave_` allocates in caller's stack frame and must be at tail position

### Common Patterns

```ocaml
(* Process local data without allocation *)
let sum_pairs (pairs @ local) =
  List.fold_left (fun acc (a, b) -> acc + a + b) 0 pairs

(* Return local from function *)
let make_pa

Related in General