either
lib.types.either
Nixpkgs manual
types.either t1 t2
Type t1 or type t2, e.g. with types; either int str.
Multiple definitions cannot be merged.
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 like attrTag instead.
Noogle detected
Implementation
The following is the current implementation of this function.
either =
t1: t2:
mkOptionType rec {
name = "either";
description =
if t1.descriptionClass or null == "nonRestrictiveClause" then
# Plain, but add comma
"${t1.description}, or ${
optionDescriptionPhrase (class: class == "noun" || class == "conjunction") t2
}"
else
"${optionDescriptionPhrase (class: class == "noun" || class == "conjunction") t1} or ${
optionDescriptionPhrase (
class: class == "noun" || class == "conjunction" || class == "composite"
) t2
}";
descriptionClass = "conjunction";
check = {
__functor = _self: x: t1.check x || t2.check x;
isV2MergeCoherent = true;
};
merge = {
__functor =
self: loc: defs:
(self.v2 { inherit loc defs; }).value;
v2 =
{ loc, defs }@args:
let
t1CheckedAndMerged =
if t1.merge ? v2 then
checkV2MergeCoherence loc t1 (t1.merge.v2 args)
else
{
value = t1.merge loc defs;
headError = checkDefsForError t1.check loc defs;
valueMeta = { };
};
t2CheckedAndMerged =
if t2.merge ? v2 then
checkV2MergeCoherence loc t2 (t2.merge.v2 args)
else
{
value = t2.merge loc defs;
headError = checkDefsForError t2.check loc defs;
valueMeta = { };
};
checkedAndMerged =
if t1CheckedAndMerged.headError == null then
t1CheckedAndMerged
else if t2CheckedAndMerged.headError == null then
t2CheckedAndMerged
else
rec {
valueMeta = {
inherit headError;
};
headError = {
message = "The option `${showOption loc}` is neither a value of type `${t1.description}` nor `${t2.description}`, Definition values: ${showDefs defs}";
};
value = lib.warn ''
One or more definitions did not pass the type-check of the 'either' type.
${headError.message}
If `either`, `oneOf` or similar is used in freeformType, ensure that it is preceded by an 'attrsOf' such as: `freeformType = types.attrsOf (types.either t1 t2)`.
Otherwise consider using the correct type for the option `${showOption loc}`. This will be an error in Nixpkgs 26.05.
'' (mergeOneOption loc defs);
};
in
checkedAndMerged;
};
typeMerge =
f':
let
mt1 = t1.typeMerge (head f'.payload.elemType).functor;
mt2 = t2.typeMerge (elemAt f'.payload.elemType 1).functor;
in
if (name == f'.name) && (mt1 != null) && (mt2 != null) then functor.type mt1 mt2 else null;
functor = elemTypeFunctor name {
elemType = [
t1
t2
];
};
nestedTypes.left = t1;
nestedTypes.right = t2;
};