Skip to content

Commit 553d8e9

Browse files
committed
doc/option-types: Improve union type docs
1 parent 1bb69a0 commit 553d8e9

File tree

1 file changed

+12
-0
lines changed

1 file changed

+12
-0
lines changed

nixos/doc/manual/development/option-types.section.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,22 +316,34 @@ A union of types is a type such that a value is valid when it is valid for at le
316316

317317
If some values are instances of more than one of the types, it is not possible to distinguish which type they are meant to be instances of. If that's needed, consider using a [sum type](#sec-option-types-sums).
318318

319+
<!-- SYNC WITH oneOf BELOW -->
319320
`types.either` *`t1 t2`*
320321

321322
: Type *`t1`* or type *`t2`*, e.g. `with types; either int str`.
322323
Multiple definitions cannot be merged.
323324

325+
::: {.warning}
326+
`either` and `oneOf` eagerly decide the active type based on the passed types' shallow check method. For composite types like `attrsOf` and `submodule`, which both match all attribute set definitions, the first type argument will be chosen for the returned option value, and this therefore also decides how nested values are checked and merged. For example, `either (attrsOf int) (submodule {...})` will always use `attrsOf int` for any attribute set value, even if it was intended as a submodule. This behavior is a trade-off that keeps the implementation simple and the evaluation order predictable, avoiding unexpected strictness problems such as infinite recursions. When proper type discrimination is needed, consider using a [sum type](#sec-option-types-sums) like `attrTag` instead.
327+
:::
328+
329+
<!-- SYNC WITH either ABOVE -->
324330
`types.oneOf` \[ *`t1 t2`* ... \]
325331

326332
: Type *`t1`* or type *`t2`* and so forth, e.g.
327333
`with types; oneOf [ int str bool ]`. Multiple definitions cannot be
328334
merged.
329335

336+
::: {.warning}
337+
`either` and `oneOf` eagerly decide the active type based on the passed types' shallow check method. For composite types like `attrsOf` and `submodule`, which both match all attribute set definitions, the first matching type in the list will be chosen for the returned option value, and this therefore also decides how nested values are checked and merged. For example, `oneOf [ (attrsOf int) (submodule {...}) ]` will always use `attrsOf int` for any attribute set value, even if it was intended as a submodule. This behavior is a trade-off that keeps the implementation simple and the evaluation order predictable, avoiding unexpected strictness problems such as infinite recursions. When proper type discrimination is needed, consider using a [sum type](#sec-option-types-sums) like `attrTag` instead.
338+
:::
339+
330340
`types.nullOr` *`t`*
331341

332342
: `null` or type *`t`*. Multiple definitions are merged according to
333343
type *`t`*.
334344

345+
This is mostly equivalent to `either (enum [ null ]) t`, but `nullOr` provides a `null` fallback for attribute values with `mkIf false` definitions in `lazyAttrsOf (nullOr t)`, whereas `either` would throw an error when the attribute is accessed.
346+
335347

336348
## Sum types {#sec-option-types-sums}
337349

0 commit comments

Comments
 (0)