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
Installation
Details
Usage
After installing, this skill will be available to your AI coding assistant.
Verify installation:
npx agent-skills-cli listSkill Instructions
name: oxcaml description: 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 license: ISC
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.
Detailed Guides
For in-depth coverage of each feature, see:
| Feature | Guide |
|---|---|
| Modes (local, unique, once, portable, contended) | SKILL-MODES.md |
| Stack Allocation (local_, stack_, exclave_) | SKILL-STACK-ALLOCATION.md |
| Unboxed Types (float#, int32#, mixed blocks) | SKILL-UNBOXED.md |
| Kinds (value, float64, bits32, kind products) | SKILL-KINDS.md |
| Uniqueness (unique/aliased, once/many) | SKILL-UNIQUENESS.md |
| Comprehensions (list/array builders) | SKILL-COMPREHENSIONS.md |
| SIMD (vector types, SSE/AVX intrinsics) | SKILL-SIMD.md |
| Templates (ppx_template, mangling) | SKILL-TEMPLATES.md |
| Zero-Alloc ([@zero_alloc] checking) | SKILL-ZERO-ALLOC.md |
| Base Library (OxCaml extensions) | SKILL-BASE.md |
| Core Library (OxCaml extensions) | SKILL-CORE.md |
Quick Reference: Syntax Cheat Sheet
(* 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 *)
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
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
(* 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β€nonportableuncontendedβ€sharedβ€contended
2. Stack Allocation (Locality)
Stack-allocated values avoid GC overhead but cannot escape their scope.
Key Constructs
(* 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
- Local values CANNOT escape their defining scope (no storing in globals, no returning without
exclave_) - Local values CAN reference global values
- Global values CANNOT reference local values
exclave_allocates in caller's stack frame and must be at tail position
Common Patterns
(* 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_pair x y = exclave_ stack_ (x, y)
(* Local references for accumulators *)
let count_positives lst =
let local_ r = ref 0 in
List.iter (fun x -> if x > 0 then r := !r + 1) lst;
!r
3. Unboxed Types
Unboxed types store values directly without heap allocation.
Built-in Unboxed Types
(* Numeric types - # suffix means unboxed *)
float# (* 64-bit float, kind float64 *)
int32# (* 32-bit int, kind bits32 *)
int64# (* 64-bit int, kind bits64 *)
nativeint# (* native int, kind word *)
float32# (* 32-bit float, kind float32 *)
int8# (* 8-bit int - untagged *)
int16# (* 16-bit int - untagged *)
int# (* native int - untagged *)
char# (* 8-bit char - untagged, same layout as int8# *)
(* Literals use # prefix *)
let x : float# = #3.14
let y : int32# = #42l
let z : int64# = #100L
let w : float32# = #1.0s
let a : int8# = #42s (* int8# literal *)
let b : int16# = #42S (* int16# literal *)
let c : char# = #'x' (* char# literal *)
(* Boxed versions (heap-allocated) *)
let a : float = 3.14 (* boxed *)
let b : float# = #3.14 (* unboxed *)
Untagged Int Arrays (New in 5.2.0minus-25)
Arrays of untagged types are packed for memory efficiency:
(* Untagged int arrays - tightly packed *)
let bytes : int8# array = [| #0s; #1s; #255s |]
let shorts : int16# array = [| #0S; #1S; #32767S |]
let ints : int# array = [| #0; #1; #42 |]
let chars : char# array = [| #'a'; #'b'; #'c' |]
(* int8# array: 1 byte per element *)
(* int16# array: 2 bytes per element *)
(* int# array: native word size per element *)
Unboxed Records
(* Unboxed record - stored inline, not heap-allocated *)
type point = #{ x : float#; y : float# }
(* Create unboxed record *)
let p : point = #{ x = #1.0; y = #2.0 }
(* Access fields *)
let get_x (p : point) = p.#x
Unboxed Tuples
(* Unboxed tuple syntax *)
type pair = #(float# * int32#)
let p : #(float# * int32#) = #(#1.0, #42l)
Mixed Blocks
Records can mix boxed and unboxed fields:
type mixed = {
name : string; (* boxed *)
value : float#; (* unboxed, stored flat *)
count : int32#; (* unboxed *)
}
or_null Type
Non-allocating option for nullable values:
type 'a or_null = Null | This of 'a
(* Use for optional unboxed values without allocation *)
let find_float arr idx : float# or_null =
if idx < Array.length arr then This arr.(idx)
else Null
4. Kinds
Kinds classify types by their runtime representation.
Kind Hierarchy
any (* any layout *)
βββ value (* standard OCaml boxed values *)
βββ float64 (* 64-bit floats *)
βββ float32 (* 32-bit floats *)
βββ bits32 (* 32-bit integers *)
βββ bits64 (* 64-bit integers *)
βββ word (* native word size *)
βββ void (* uninhabited *)
Kind Annotations
(* On type parameters *)
type ('a : float64) container = ...
(* On type variables in signatures *)
val f : ('a : value). 'a -> 'a
val g : ('a : bits64). 'a -> 'a
(* On abstract types *)
type t : float64
(* Kind products for unboxed tuples *)
type pair : float64 & bits32 (* unboxed pair of float# and int32# *)
Kind Abbreviations
value = value_or_null mod non_null separable
immediate = value mod external_
immediate64 = value mod external64
mutable_data = value mod non_float
immutable_data = value mod non_float immutable
Mode Bounds on Kinds
Kinds can specify which modes a type crosses:
(* Type that cannot be used at mode local *)
type t : value mod global
(* Type that is always portable *)
type t : value mod portable
5. Uniqueness
Track values with exactly one reference for safe mutation/deallocation.
Modes
unique: Single reference existsaliased: Multiple references may exist
Syntax
(* Unique parameter - consumed by function *)
val free : t @ unique -> unit
(* Aliased return - may have multiple references *)
val duplicate : t -> t * t @ aliased
(* Once closures - can only be invoked once *)
val delay_free : t @ unique -> (unit -> unit) @ once
Uniqueness Rules
(* OK: match then use uniquely *)
let ok t =
match t with
| Con { field } -> free t
(* ERROR: using parts twice *)
let bad t =
match t with
| Con { field } ->
free_field field; (* uses field *)
free t (* uses t which contains field *)
(* OK: different branches *)
let ok t =
match t with
| Con { field } ->
if cond then free_field field
else free t
Aliased Modality
Store aliased values in unique containers:
type 'a aliased_box = { value : 'a @@ aliased } [@@unboxed]
(* Container is unique but contents are aliased *)
val push : 'a @ aliased -> 'a aliased_box list @ unique -> 'a aliased_box list @ unique
6. Comprehensions
Python/Haskell-style list and array builders.
List Comprehensions
(* Basic *)
[ x * 2 for x = 1 to 10 ]
(* With filter *)
[ x for x = 1 to 100 when x mod 2 = 0 ]
(* Nested iteration *)
[ (x, y) for x = 1 to 3 for y = 1 to 3 ]
(* Iterate over list *)
[ String.uppercase s for s in strings ]
(* Multiple conditions *)
[ x + y for x = 1 to 10 for y = 1 to 10 when x < y when x + y < 15 ]
(* Parallel iteration (evaluated together) *)
[ x + y for x = 1 to 3 and y = 10 to 12 ]
Array Comprehensions
(* Same syntax with [| |] *)
[| x * x for x = 1 to 10 |]
(* Iterate over array *)
[| f elem for elem in source_array |]
Immutable Array Comprehensions
[: x for x = 1 to 10 when x mod 2 = 0 :]
Key Differences: for vs and
for ... for ...: Nested (inner re-evaluated each outer iteration)for ... and ...: Parallel (both evaluated once upfront)
(* Nested: 9 elements *)
[ (x, y) for x = 1 to 3 for y = 1 to 3 ]
(* Parallel: 3 elements *)
[ (x, y) for x = 1 to 3 and y = 10 to 12 ]
(* = [(1,10); (2,11); (3,12)] *)
7. SIMD Vector Types
128-bit and 256-bit SIMD vectors for parallel numeric operations.
Types
(* 128-bit vectors *)
int8x16 int8x16# (* 16 x 8-bit ints *)
int16x8 int16x8# (* 8 x 16-bit ints *)
int32x4 int32x4# (* 4 x 32-bit ints *)
int64x2 int64x2# (* 2 x 64-bit ints *)
float32x4 float32x4# (* 4 x 32-bit floats *)
float64x2 float64x2# (* 2 x 64-bit floats *)
(* 256-bit vectors *)
int8x32 int8x32#
int32x8 int32x8#
float64x4 float64x4#
(* etc. *)
Usage
open Ocaml_simd_sse
let v = Float32x4.set 1.0 2.0 3.0 4.0
let v = Float32x4.sqrt v
let x, y, z, w = Float32x4.splat v
(* Load from arrays *)
let v = Int8x16.String.get text ~byte:0
C Stubs
external vec_op : (int8x16[@unboxed]) -> (int8x16[@unboxed]) =
"boxed_stub" "unboxed_stub"
8. Templates (ppx_template)
Generate multiple copies of code with different modes/kinds.
Mode Templates
(* Define once, get local and global versions *)
let%template[@mode m = (global, local)] id
: 'a. 'a @ m -> 'a @ m
= fun x -> x
(* Generates: id (global) and id__local *)
(* Instantiate *)
let f x = (id [@mode local]) x
Kind Templates
let%template[@kind k = (value, float64)] id
: ('a : k). 'a -> 'a
= fun x -> x
(* Generates: id (value) and id__float64 *)
Exclave Conditional
let%template[@mode m = (global, local)] make_pair x y =
(x, y) [@exclave_if_local m]
(* local version gets: exclave_ (x, y) *)
Alloc Templates
let%template rec map
: f:('a -> 'b @ m) -> 'a list -> 'b list @ m
= fun ~f list ->
match[@exclave_if_stack a] list with
| [] -> []
| hd :: tl -> f hd :: (map [@alloc a]) ~f tl
[@@alloc a @ m = (heap_global, stack_local)]
Portable Functors
(* Short form for portable/nonportable functor variants *)
module%template.portable Make (M : S) : T
Default Floating Attributes
[%%template:
[@@@mode.default m = (global, local)]
val min : t @ m -> t @ m -> t @ m
val max : t @ m -> t @ m -> t @ m]
9. Zero-Alloc Checking
Compile-time verification that functions don't allocate.
Basic Usage
(* Check function doesn't allocate *)
let[@zero_alloc] fast_add x y = x + y
(* Allow local/stack allocations *)
let[@zero_alloc] with_local_pair x y =
let p = stack_ (x, y) in
fst p + snd p
(* Only check in optimized builds *)
let[@zero_alloc opt] complex_func x = ...
(* Strict: no allocation even on error paths *)
let[@zero_alloc strict] very_strict x = ...
Assume Annotations
(* Trust this function is zero-alloc *)
let[@zero_alloc assume] external_wrapper x = external_func x
(* Assume for error paths *)
let[@cold][@zero_alloc assume error] handle_error e =
log_error e;
default_value
In Signatures
val[@zero_alloc] f : int -> int
val[@zero_alloc strict] g : t -> t
val[@zero_alloc arity 2] h : int -> int -> int
File-Level
[@@@zero_alloc all] (* All functions must be zero-alloc *)
let[@zero_alloc ignore] allowed_to_alloc x = [x] (* Opt out *)
10. Parallelism & Capsules
Safe parallel programming with thread isolation.
Contention Modes
contended: May be accessed from multiple threads concurrentlyshared: May be accessed from multiple threads (for shared state)uncontended: Single-thread access
Portability Modes
portable: Safe to move across thread boundaries, captures all values at contendedshareable: May execute in parallel, captures shared statenonportable: Thread-local only, captures uncontended mutable state
Capsules (Experimental)
Capsules isolate mutable state for safe parallelism:
(* Capsule contains thread-local mutable state *)
type 'a capsule
(* Access requires entering capsule context *)
val with_capsule : 'a capsule -> ('a @ local -> 'b) -> 'b
11. Miscellaneous Extensions
Labeled Tuples
(* Create *)
let point = ~x:10, ~y:20
(* Type *)
type point = x:int * y:int
(* Destructure *)
let ~x, ~y = point
(* Partial match (needs type annotation) *)
let get_x (p : x:int * y:int) =
let ~x, .. = p in x
(* Function returning labeled tuple *)
val dimensions : image -> width:int * height:int
Immutable Arrays
(* Syntax uses : instead of | *)
let arr : string iarray = [: "a"; "b"; "c" :]
(* Access *)
let first = arr.:(0)
(* Covariant - allows safe subtyping *)
let arr2 : obj iarray = (arr : sub_obj iarray :> obj iarray)
Include Functor
(* Instead of *)
module M = struct
module T = struct
type t = ...
[@@deriving compare, sexp]
end
include T
include Comparable.Make(T)
end
(* Write *)
module M = struct
type t = ...
[@@deriving compare, sexp]
include functor Comparable.Make
end
Let Mutable
(* Mutable local variable - no allocation *)
let triangle n =
let mutable total = 0 in
for i = 1 to n do
total <- total + i
done;
total
Restrictions: Cannot escape scope, no closure capture, single variable only.
Polymorphic Parameters
(* Function taking polymorphic argument *)
let create (f : 'a. 'a field -> 'a) =
{ a = f A; b = f B }
val create : ('a. 'a field -> 'a) -> t
Small Numbers
(* Types *)
float32 float32#
int8 int8#
int16 int16#
char#
(* Literals *)
1.0s (* float32 *)
#1.0s (* float32# *)
42s (* int8 *)
#42s (* int8# *)
42S (* int16 *)
#42S (* int16# *)
#'a' (* char# *)
(* Arrays - now supported and packed! *)
int8 array int8# array (* 1 byte per element *)
int16 array int16# array (* 2 bytes per element *)
char# array (* 1 byte per element *)
(* Pattern matching with char# ranges *)
match c with
| #'a'..#'z' -> `lowercase
| #'A'..#'Z' -> `uppercase
| _ -> `other
Module Strengthening
(* Instead of *)
sig type t = M.t end
(* Write *)
S with M
Common Patterns
Zero-Alloc Hot Path
let[@zero_alloc] process_batch (data @ local) =
let local_ acc = ref 0 in
for i = 0 to Array.length data - 1 do
acc := !acc + process_item data.(i)
done;
!acc
Local Allocation in Loop
let process_all items =
List.iter (fun item ->
let local_ temp = compute item in
use temp
) items
Unique Resource Management
type handle
val open_handle : unit -> handle @ unique
val use_handle : handle @ unique -> result * handle @ unique
val close_handle : handle @ unique -> unit
let with_handle f =
let h = open_handle () in
let result, h = use_handle h in
close_handle h;
result
Mode-Polymorphic Function
let%template[@mode m = (global, local)] map_pair f (a, b) =
((f a, f b) [@exclave_if_local m])
Kind-Polymorphic Container
type%template ('a : k) box = { contents : 'a }
[@@kind k = (value, float64, bits64)]
Debugging Tips
- Mode errors: Check if you're trying to return local data globally
- Kind errors: Ensure type parameters have correct layout annotations
- Zero-alloc failures: Use
-zero-alloc-checker-details-cutoff -1for full details - Template issues: Check mangled names with
__suffixpattern
Library Dependencies
Core Libraries
stdlib_stable: Immutable arrays (Iarray),Float32,Int8,Int16,Char_ubase: Jane Street's standard library with comprehensive OxCaml mode support- IMPORTANT: Consult SKILL-BASE.md for OxCaml-friendly functions!
- Contains 116 modules with extensive local/exclave, mode, and unboxed type support
- Key modules:
Modes(modal wrappers),Iarray(immutable arrays with local ops),Container_with_local, and__localvariants of most collection functions
core: Extended library with I/O, async, and system features- See SKILL-CORE.md for Iobuf, Time_ns, Bigstring extensions
PPX Libraries
ppx_template: Mode/kind polymorphism via code generation- See SKILL-TEMPLATES.md for mangling details
ppx_simd: SIMD shuffle/blend mask generation
SIMD Libraries
ocaml_simd: Base SIMD typesocaml_simd_sse: SSE intrinsics (128-bit)ocaml_simd_avx: AVX/AVX2 intrinsics (256-bit)- See SKILL-SIMD.md for usage details
More by aresbit
View allUse when you need to control Slack from OpenClaw via the slack tool, including reacting to messages or pinning/unpinning items in Slack channels or DMs.
Use the ClawdHub CLI to search, install, update, and publish agent skills from clawdhub.com. Use when you need to fetch new skills on the fly, sync installed skills to latest or a specific version, or publish new/updated skill folders with the npm-installed clawdhub CLI.
Search GIF providers with CLI/TUI, download results, and extract stills/sheets.
Fixing odoc documentation warnings and errors. Use when running dune build @doc, resolving reference syntax issues, cross-package references, ambiguous references, hidden fields, or @raise tags in OCaml documentation.
