Option Operations Representation
Depends On: Native Type Universe § 5.1 Option
1. Overview
Operand-demand rules in this chapter describe ordinary deferred arguments.
Direct explicit eager operands are demanded
at their activated application/formation frontier, even where the selected
Option case would not otherwise need them. Their completed values are shared,
not replayed at a later partial stage or callback invocation.
Clef implements option operations (Option.map, Option.bind, Option.defaultValue, etc.) as Baker-decomposed pattern matches. On layout-realizing pathways, options are inline tagged values; any materialized storage follows the owning lifetime contract. Their operations elaborate to selected-case control flow without requiring a separately allocated managed option object.
Key Insight: Option operations are structurally trivial: each is a single match expression with two branches (Some/None). Baker decomposes them to isSome checks and value extraction.
2. Memory Layout (Reference)
Options follow the layout specified in Native Type Universe § 5.1:
option<'T> (voption semantics)
┌─────────────┬────────────────────┐
│ Tag (≥ i8) │ Payload: 'T │
└─────────────┴────────────────────┘
≥1 byte sizeof<'T> + padding| Property | Value |
|---|---|
| Tag values | None = 0, Some = 1 |
| Tag width | Platform policy; minimum i8 (see DU Representation § 2.2.1) |
| Stack allocated | Always (never heap) |
| Null-freedom | None is tag 0, NOT null pointer |
Note: Option has 2 cases, so the minimum tag is i8 (1 byte). Platform policy MAY use larger tags for alignment efficiency on word-aligned architectures. The tag is NEVER i1 (bit) because bits are not addressable and DU tags are case indices, not booleans.
2.1 JSIR-Pathway Realization
This section binds implementations claiming the JavaScript Substrate profile (Conformance §7).
The layout above is a commitment of layout-realizing pathways. On the JSIR pathway, under the carrier-realization rule of Backend Lowering Architecture §4.5, option<'T> is realized in one of two forms, selected per instantiation at compile time:
- Erased:
Some xis realized as the realization ofx, andNoneasundefined. The erased form SHALL be selected only where the implementation proves thatundefinedcannot denote aSomepayload: the payload type’s realization SHALL NOT includeundefinedamong its values, and a nestedoptionSHALL be reified at every nesting level the proof cannot discharge. - Reified: a host object carrying the §2 tag semantics (
None= 0,Some= 1) and the payload. The reified form is always permitted.
The selection is a compile-time property of each instantiation, settled before emission and read at emission; it SHALL NOT vary at runtime. The observable semantics of §3 and §4 (operation results, lazy defaults, vacuous truth) bind unchanged under either form.
Erasure is an interior representation choice, not a boundary conversion. Absence crossing the JavaScript boundary is converted under JavaScript Boundary Semantics §5, and an erased None that reaches an outbound boundary position is emitted as that position’s selected representation, which need not be undefined.
3. Operation Classification
3.1 Primitive Operations (Alex Witnesses Directly)
| Operation | Signature | MLIR Generation |
|---|---|---|
Option.None | unit -> 'T option | Create struct with tag=0 |
Option.Some | 'T -> 'T option | Create struct with tag=1, value |
Option.isSome | 'T option -> bool | Extract tag, compare to 1 |
Option.isNone | 'T option -> bool | Extract tag, compare to 0 |
Option.get | 'T option -> 'T | Project the selected payload under an established same-value Some premise or admitted failure contract; no unchecked absent-payload read |
3.2 Higher-Order Functions (Baker Decomposes)
| Category | Operations | Description |
|---|---|---|
| Transformers | map, map2, map3 | Apply function if Some |
| Binders | bind, flatten | Chain optional computations |
| Defaults | defaultValue, defaultWith, orElse, orElseWith | Provide fallback values |
| Folds | fold, foldBack | Eliminate an optional payload into an independently typed state |
| Predicates | filter, exists, forall | Conditional Some/None |
| Conversion | toList, toArray, toNullable, ofNullable | Type conversions |
| Iteration | iter | Side-effect if Some |
4. HOF Decomposition Specifications
4.1 Option.map
Option.map : ('a -> 'b) -> 'a option -> 'b optionDecomposition:
let map f opt =
match opt with
| Some x -> Some (f x)
| None -> NoneBaker Recipe:
recipe {
let! isSome = isSome optId
let! someCase = recipe {
let! value = getValue optId elemType
let! mapped = app1 mapperNodeId value outputType
return! some mapped outputType
}
let! noneCase = none outputType
return! ifThenElse isSome someCase noneCase optionOutputType
}4.2 Option.bind
Option.bind : ('a -> 'b option) -> 'a option -> 'b optionDecomposition:
let bind f opt =
match opt with
| Some x -> f x
| None -> NoneBaker Recipe: Similar to map, but f x returns an option directly (no wrapping).
4.3 Option.defaultValue
Option.defaultValue : 'a -> 'a option -> 'aDecomposition:
let defaultValue def opt =
match opt with
| Some x -> x
| None -> defBaker Recipe: Retain the input’s shared identity, test its case, and place payload projection inside the selected Some body. The None body retains the fallback’s shared computation. The payload read is not unconditional setup for the decision, and neither a tag test nor the unselected arm forces a payload.
4.4 Option.defaultWith
Option.defaultWith : (unit -> 'a) -> 'a option -> 'aDecomposition:
let defaultWith defThunk opt =
match opt with
| Some x -> x
| None -> defThunk ()Note: The thunk is only evaluated if opt is None (lazy evaluation).
4.5 Option.orElse
Option.orElse : 'a option -> 'a option -> 'a optionDecomposition:
let orElse ifNone opt =
match opt with
| Some _ -> opt
| None -> ifNone4.6 Option.orElseWith
Option.orElseWith : (unit -> 'a option) -> 'a option -> 'a optionDecomposition:
let orElseWith ifNoneThunk opt =
match opt with
| Some _ -> opt
| None -> ifNoneThunk ()4.7 Option.filter
Option.filter : ('a -> bool) -> 'a option -> 'a optionDecomposition:
let filter predicate opt =
match opt with
| Some x -> if predicate x then Some x else None
| None -> NoneBaker Recipe:
recipe {
let! isSome = isSome optId
let! filteredSome = recipe {
let! value = getValue optId elemType
let! passes = app1 predicateId value boolType
let! keepSome = some value elemType
let! discardNone = none elemType
return! ifThenElse passes keepSome discardNone optionType
}
let! noneCase = none elemType
return! ifThenElse isSome filteredSome noneCase optionType
}4.8 Option.exists
Option.exists : ('a -> bool) -> 'a option -> boolDecomposition:
let exists predicate opt =
match opt with
| Some x -> predicate x
| None -> false4.9 Option.forall
Option.forall : ('a -> bool) -> 'a option -> boolDecomposition:
let forall predicate opt =
match opt with
| Some x -> predicate x
| None -> true // Vacuously true
4.10 Option.iter
Option.iter : ('a -> unit) -> 'a option -> unitDecomposition:
let iter action opt =
match opt with
| Some x -> action x
| None -> ()4.11 Option.toList
Option.toList : 'a option -> 'a listDecomposition:
let toList opt =
match opt with
| Some x -> [x]
| None -> []4.12 Option.toArray
Option.toArray : 'a option -> 'a arrayDecomposition:
let toArray opt =
match opt with
| Some x -> [| x |]
| None -> [| |]4.13 Option.flatten
Option.flatten : 'a option option -> 'a optionDecomposition:
let flatten opt =
match opt with
| Some inner -> inner
| None -> None4.14 Option.map2
Option.map2 : ('a -> 'b -> 'c) -> 'a option -> 'b option -> 'c optionDecomposition:
let map2 f opt1 opt2 =
match opt1, opt2 with
| Some x, Some y -> Some (f x y)
| _ -> None4.15 Option.map3
Option.map3 : ('a -> 'b -> 'c -> 'd) -> 'a option -> 'b option -> 'c option -> 'd optionDecomposition:
let map3 f opt1 opt2 opt3 =
match opt1, opt2, opt3 with
| Some x, Some y, Some z -> Some (f x y z)
| _ -> None4.16 Option.fold
Option.fold : ('State -> 'T -> 'State) -> 'State -> 'T option -> 'Statelet fold folder state opt =
match opt with
| Some value -> folder state value
| None -> stateThe state and payload are independently quantified NTU types. Their dimensional
and resource relationships SHALL be retained at the folder’s two argument edges
and its result edge. The result has the supplied state’s type; there is no
implicit widening or dimensional conversion between state and payload.
Explicit type arguments use Option.fold<'State, 'T>.
4.17 Option.foldBack
Option.foldBack : ('T -> 'State -> 'State) -> 'T option -> 'State -> 'Statelet foldBack folder opt state =
match opt with
| Some value -> folder value state
| None -> stateExplicit type arguments use Option.foldBack<'State, 'T> in the same state,
payload order as fold.
Both folds follow default demand and sharing.
Demanding the fold result requires the option’s case. For Some, demand the
folder’s result with the argument order shown; the folder determines demand for
the state and payload. For None, return the supplied state’s shared computation
without demanding or invoking the folder. A demanded state result is evaluated
once through that shared identity. Supplying ordinary operands or forming a partial
application SHALL NOT force unused folder, state or payload expressions.
Partial applications retain already supplied values or deferred identities;
captured storage retains its existing identity and lifetime obligations.
The declared three-argument operation boundary SHALL remain distinct from any subsequent application of a function-valued state result. Bare aliases SHALL instantiate the state and payload types independently at each admitted use. Baker SHALL elaborate these relationships into the graph before Alex witnesses the resulting conditional and calls; neither fold introduces an Alex intrinsic or relaxes admission requirements for its state, payload or folder.
5. SSA Cost Formulas
The following counts illustrate one fully demanded scalar lowering using
undef, insertvalue and extractvalue notation. Baker SHALL settle demand,
case guards and typed storage; Alex SHALL create the actual SSA operands through
Elements, Patterns and Witnesses in the admitted portable dialects. Cost
accounting SHALL use the operations and storage of the selected realization,
including deferred payloads, aggregate layouts and target lowering.
| Operation | SSA Operations |
|---|---|
None | 2 (undef + insertvalue tag=0) |
Some v | 3 (undef + insertvalue tag=1 + insertvalue value) |
isSome | 2 (extractvalue + icmp) |
isNone | 2 (extractvalue + icmp) |
getValue | 1 (extractvalue) |
map | 6 + C_mapper (isSome + branch + getValue + apply + wrap) |
bind | 5 + C_binder (isSome + branch + getValue + apply) |
defaultValue | 4 (isSome + branch + getValue or default) |
filter | 8 + C_predicate (nested conditionals) |
6. Normative Requirements
- Inline Value Representation: On a pathway that realizes memory layouts, option values SHALL use their admitted tagged value representation without requiring a separately allocated managed option object. Materialized values and retained payloads SHALL follow the containing value’s proved lifetime and storage authority under Discriminated Union Representation §8.2; a returned, captured or cached option SHALL NOT retain storage that expires before its uses.
- Tag Encoding: On a pathway that realizes memory layouts,
None= 0,Some= 1; tag width per platform policy (minimumi8) - No Null:
NoneSHALL NOT be represented as a null pointer; on a layout-realizing pathway it is a valid struct with tag=0, and on the JSIR pathway an erasedNoneisundefinedper §2.1, nevernull - Decomposition: Option HOFs SHALL be decomposed by Baker to primitive operations
- Lazy Defaults:
defaultWithandorElseWithSHALL only evaluate thunk when needed - Vacuous Truth:
Option.forallonNoneSHALL returntrue - Tag Is Not Boolean: On a pathway that realizes memory layouts, tag MUST be at least
i8, NEVERi1, because tags are case indices, not truth values - JSIR-Pathway Realization: On the JSIR pathway,
option<'T>SHALL be realized erased or reified per §2.1, with the erased form selected only under the §2.1 proof; requirements 4, 5, and 6 bind on every pathway
7. Relationship to Result
Option and Result are both sum types with similar operation patterns:
| Option | Result | Semantic Difference |
|---|---|---|
Some x | Ok x | Success case |
None | Error e | Failure case (Option has no error info) |
Option.map | Result.map | Transform success |
Option.bind | Result.bind | Chain computations |
Option.defaultValue | Result.defaultValue | Fallback on failure |
When to use Option: Absence of value (lookup miss, empty input).
When to use Result: Failure with explanation (validation error, IO failure).
8. References
- Native Type Universe § 5.1 Option - Option memory layout
- Baker OptionRecipes.fs - Implementation reference