query
On this page

either

lib.types.either

Docs pulled from | This Revision | 31 minutes ago


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;
    };