Merge pull request #181834 from ncfavier/numbers

lib/types: add `number`

Changed files
+335 -228
lib
nixos
doc
manual
development
from_md
modules
services
x11
+33 -9
lib/types.nix
···
};
int = mkOptionType {
-
name = "int";
-
description = "signed integer";
-
check = isInt;
-
merge = mergeEqualOption;
-
};
+
name = "int";
+
description = "signed integer";
+
check = isInt;
+
merge = mergeEqualOption;
+
};
# Specialized subdomains of int
ints =
···
port = ints.u16;
float = mkOptionType {
-
name = "float";
-
description = "floating point number";
-
check = isFloat;
-
merge = mergeEqualOption;
+
name = "float";
+
description = "floating point number";
+
check = isFloat;
+
merge = mergeEqualOption;
+
};
+
+
number = either int float;
+
+
numbers = let
+
betweenDesc = lowest: highest:
+
"${builtins.toJSON lowest} and ${builtins.toJSON highest} (both inclusive)";
+
in {
+
between = lowest: highest:
+
assert lib.assertMsg (lowest <= highest)
+
"numbers.between: lowest must be smaller than highest";
+
addCheck number (x: x >= lowest && x <= highest) // {
+
name = "numberBetween";
+
description = "integer or floating point number between ${betweenDesc lowest highest}";
+
};
+
+
nonnegative = addCheck number (x: x >= 0) // {
+
name = "numberNonnegative";
+
description = "nonnegative integer or floating point number, meaning >=0";
+
};
+
positive = addCheck number (x: x > 0) // {
+
name = "numberPositive";
+
description = "positive integer or floating point number, meaning >0";
+
};
};
str = mkOptionType {
+49 -24
nixos/doc/manual/development/option-types.section.md
···
can take. Types are also responsible of how values are merged in case of
multiple value definitions.
-
## Basic Types {#sec-option-types-basic}
+
## Basic types {#sec-option-types-basic}
Basic types are the simplest available types in the module system. Basic
types include multiple string types that mainly differ in how definition
···
: A top-level store path. This can be an attribute set pointing
to a store path, like a derivation or a flake input.
+
+
`types.enum` *`l`*
+
+
: One element of the list *`l`*, e.g. `types.enum [ "left" "right" ]`.
+
Multiple definitions cannot be merged.
`types.anything`
···
problems.
:::
-
Integer-related types:
+
### Numeric types {#sec-option-types-numeric}
`types.int`
···
from 0 to 2^n−1 respectively (e.g. `0`
to `255` for 8 bits).
+
`types.ints.between` *`lowest highest`*
+
+
: An integer between *`lowest`* and *`highest`* (both inclusive).
+
`types.ints.positive`
: A positive integer (that is > 0).
···
: A port number. This type is an alias to
`types.ints.u16`.
-
String-related types:
+
`types.float`
+
+
: A floating point number.
+
+
::: {.warning}
+
Converting a floating point number to a string with `toString` or `toJSON`
+
may result in [precision loss](https://github.com/NixOS/nix/issues/5733).
+
:::
+
+
`types.number`
+
+
: Either a signed integer or a floating point number. No implicit conversion
+
is done between the two types, and multiple equal definitions will only be
+
merged if they have the same type.
+
+
`types.numbers.between` *`lowest highest`*
+
+
: An integer or floating point number between *`lowest`* and *`highest`* (both inclusive).
+
+
`types.numbers.nonnegative`
+
+
: A nonnegative integer or floating point number (that is >= 0).
+
+
`types.numbers.positive`
+
+
: A positive integer or floating point number (that is > 0).
+
+
### String types {#sec-option-types-string}
`types.str`
: A string. Multiple definitions cannot be merged.
+
`types.separatedString` *`sep`*
+
+
: A string. Multiple definitions are concatenated with *`sep`*, e.g.
+
`types.separatedString "|"`.
+
`types.lines`
: A string. Multiple definitions are concatenated with a new line
···
`types.envVar`
-
: A string. Multiple definitions are concatenated with a collon `":"`.
+
: A string. Multiple definitions are concatenated with a colon `":"`.
`types.strMatching`
···
definitions cannot be merged. The regular expression is processed
using `builtins.match`.
-
## Value Types {#sec-option-types-value}
-
-
Value types are types that take a value parameter.
-
-
`types.enum` *`l`*
-
-
: One element of the list *`l`*, e.g. `types.enum [ "left" "right" ]`.
-
Multiple definitions cannot be merged.
-
-
`types.separatedString` *`sep`*
-
-
: A string with a custom separator *`sep`*, e.g.
-
`types.separatedString "|"`.
+
## Submodule types {#sec-option-types-submodule}
-
`types.ints.between` *`lowest highest`*
-
-
: An integer between *`lowest`* and *`highest`* (both inclusive). Useful
-
for creating types like `types.port`.
+
Submodules are detailed in [Submodule](#section-option-types-submodule).
`types.submodule` *`o`*
···
value. Submodules are used in composed types to create modular
options. This is equivalent to
`types.submoduleWith { modules = toList o; shorthandOnlyDefinesConfig = true; }`.
-
Submodules are detailed in [Submodule](#section-option-types-submodule).
`types.submoduleWith` { *`modules`*, *`specialArgs`* ? {}, *`shorthandOnlyDefinesConfig`* ? false }
···
more convenient and discoverable than expecting the module user to
type-merge with the `attrsOf submodule` option.
-
## Composed Types {#sec-option-types-composed}
+
## Composed types {#sec-option-types-composed}
Composed types are types that take a type as parameter. `listOf
int` and `either int str` are examples of composed types.
···
of strings, and `defs` the list of defined values as a list. It is
possible to override a type merge function for custom needs.
-
## Custom Types {#sec-option-types-custom}
+
## Custom types {#sec-option-types-custom}
Custom types can be created with the `mkOptionType` function. As type
creation includes some more complex topics such as submodule handling,
+248 -181
nixos/doc/manual/from_md/development/option-types.section.xml
···
in case of multiple value definitions.
</para>
<section xml:id="sec-option-types-basic">
-
<title>Basic Types</title>
+
<title>Basic types</title>
<para>
Basic types are the simplest available types in the module system.
Basic types include multiple string types that mainly differ in
···
A top-level store path. This can be an attribute set
pointing to a store path, like a derivation or a flake
input.
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.enum</literal>
+
<emphasis><literal>l</literal></emphasis>
+
</term>
+
<listitem>
+
<para>
+
One element of the list
+
<emphasis><literal>l</literal></emphasis>, e.g.
+
<literal>types.enum [ &quot;left&quot; &quot;right&quot; ]</literal>.
+
Multiple definitions cannot be merged.
</para>
</listitem>
</varlistentry>
···
</listitem>
</varlistentry>
</variablelist>
-
<para>
-
Integer-related types:
-
</para>
-
<variablelist>
-
<varlistentry>
-
<term>
-
<literal>types.int</literal>
-
</term>
-
<listitem>
-
<para>
-
A signed integer.
-
</para>
-
</listitem>
-
</varlistentry>
-
<varlistentry>
-
<term>
-
<literal>types.ints.{s8, s16, s32}</literal>
-
</term>
-
<listitem>
-
<para>
-
Signed integers with a fixed length (8, 16 or 32 bits). They
-
go from −2^n/2 to 2^n/2−1 respectively (e.g.
-
<literal>−128</literal> to <literal>127</literal> for 8
-
bits).
-
</para>
-
</listitem>
-
</varlistentry>
-
<varlistentry>
-
<term>
-
<literal>types.ints.unsigned</literal>
-
</term>
-
<listitem>
-
<para>
-
An unsigned integer (that is &gt;= 0).
-
</para>
-
</listitem>
-
</varlistentry>
-
<varlistentry>
-
<term>
-
<literal>types.ints.{u8, u16, u32}</literal>
-
</term>
-
<listitem>
-
<para>
-
Unsigned integers with a fixed length (8, 16 or 32 bits).
-
They go from 0 to 2^n−1 respectively (e.g.
-
<literal>0</literal> to <literal>255</literal> for 8 bits).
-
</para>
-
</listitem>
-
</varlistentry>
-
<varlistentry>
-
<term>
-
<literal>types.ints.positive</literal>
-
</term>
-
<listitem>
-
<para>
-
A positive integer (that is &gt; 0).
-
</para>
-
</listitem>
-
</varlistentry>
-
<varlistentry>
-
<term>
-
<literal>types.port</literal>
-
</term>
-
<listitem>
-
<para>
-
A port number. This type is an alias to
-
<literal>types.ints.u16</literal>.
-
</para>
-
</listitem>
-
</varlistentry>
-
</variablelist>
-
<para>
-
String-related types:
-
</para>
-
<variablelist>
-
<varlistentry>
-
<term>
-
<literal>types.str</literal>
-
</term>
-
<listitem>
-
<para>
-
A string. Multiple definitions cannot be merged.
-
</para>
-
</listitem>
-
</varlistentry>
-
<varlistentry>
-
<term>
-
<literal>types.lines</literal>
-
</term>
-
<listitem>
-
<para>
-
A string. Multiple definitions are concatenated with a new
-
line <literal>&quot;\n&quot;</literal>.
-
</para>
-
</listitem>
-
</varlistentry>
-
<varlistentry>
-
<term>
-
<literal>types.commas</literal>
-
</term>
-
<listitem>
-
<para>
-
A string. Multiple definitions are concatenated with a comma
-
<literal>&quot;,&quot;</literal>.
-
</para>
-
</listitem>
-
</varlistentry>
-
<varlistentry>
-
<term>
-
<literal>types.envVar</literal>
-
</term>
-
<listitem>
-
<para>
-
A string. Multiple definitions are concatenated with a
-
collon <literal>&quot;:&quot;</literal>.
-
</para>
-
</listitem>
-
</varlistentry>
-
<varlistentry>
-
<term>
-
<literal>types.strMatching</literal>
-
</term>
-
<listitem>
-
<para>
-
A string matching a specific regular expression. Multiple
-
definitions cannot be merged. The regular expression is
-
processed using <literal>builtins.match</literal>.
-
</para>
-
</listitem>
-
</varlistentry>
-
</variablelist>
+
<section xml:id="sec-option-types-numeric">
+
<title>Numeric types</title>
+
<variablelist>
+
<varlistentry>
+
<term>
+
<literal>types.int</literal>
+
</term>
+
<listitem>
+
<para>
+
A signed integer.
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.ints.{s8, s16, s32}</literal>
+
</term>
+
<listitem>
+
<para>
+
Signed integers with a fixed length (8, 16 or 32 bits).
+
They go from −2^n/2 to 2^n/2−1 respectively (e.g.
+
<literal>−128</literal> to <literal>127</literal> for 8
+
bits).
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.ints.unsigned</literal>
+
</term>
+
<listitem>
+
<para>
+
An unsigned integer (that is &gt;= 0).
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.ints.{u8, u16, u32}</literal>
+
</term>
+
<listitem>
+
<para>
+
Unsigned integers with a fixed length (8, 16 or 32 bits).
+
They go from 0 to 2^n−1 respectively (e.g.
+
<literal>0</literal> to <literal>255</literal> for 8
+
bits).
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.ints.between</literal>
+
<emphasis><literal>lowest highest</literal></emphasis>
+
</term>
+
<listitem>
+
<para>
+
An integer between
+
<emphasis><literal>lowest</literal></emphasis> and
+
<emphasis><literal>highest</literal></emphasis> (both
+
inclusive).
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.ints.positive</literal>
+
</term>
+
<listitem>
+
<para>
+
A positive integer (that is &gt; 0).
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.port</literal>
+
</term>
+
<listitem>
+
<para>
+
A port number. This type is an alias to
+
<literal>types.ints.u16</literal>.
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.float</literal>
+
</term>
+
<listitem>
+
<para>
+
A floating point number.
+
</para>
+
<warning>
+
<para>
+
Converting a floating point number to a string with
+
<literal>toString</literal> or <literal>toJSON</literal>
+
may result in
+
<link xlink:href="https://github.com/NixOS/nix/issues/5733">precision
+
loss</link>.
+
</para>
+
</warning>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.number</literal>
+
</term>
+
<listitem>
+
<para>
+
Either a signed integer or a floating point number. No
+
implicit conversion is done between the two types, and
+
multiple equal definitions will only be merged if they
+
have the same type.
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.numbers.between</literal>
+
<emphasis><literal>lowest highest</literal></emphasis>
+
</term>
+
<listitem>
+
<para>
+
An integer or floating point number between
+
<emphasis><literal>lowest</literal></emphasis> and
+
<emphasis><literal>highest</literal></emphasis> (both
+
inclusive).
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.numbers.nonnegative</literal>
+
</term>
+
<listitem>
+
<para>
+
A nonnegative integer or floating point number (that is
+
&gt;= 0).
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.numbers.positive</literal>
+
</term>
+
<listitem>
+
<para>
+
A positive integer or floating point number (that is &gt;
+
0).
+
</para>
+
</listitem>
+
</varlistentry>
+
</variablelist>
+
</section>
+
<section xml:id="sec-option-types-string">
+
<title>String types</title>
+
<variablelist>
+
<varlistentry>
+
<term>
+
<literal>types.str</literal>
+
</term>
+
<listitem>
+
<para>
+
A string. Multiple definitions cannot be merged.
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.separatedString</literal>
+
<emphasis><literal>sep</literal></emphasis>
+
</term>
+
<listitem>
+
<para>
+
A string. Multiple definitions are concatenated with
+
<emphasis><literal>sep</literal></emphasis>, e.g.
+
<literal>types.separatedString &quot;|&quot;</literal>.
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.lines</literal>
+
</term>
+
<listitem>
+
<para>
+
A string. Multiple definitions are concatenated with a new
+
line <literal>&quot;\n&quot;</literal>.
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.commas</literal>
+
</term>
+
<listitem>
+
<para>
+
A string. Multiple definitions are concatenated with a
+
comma <literal>&quot;,&quot;</literal>.
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.envVar</literal>
+
</term>
+
<listitem>
+
<para>
+
A string. Multiple definitions are concatenated with a
+
colon <literal>&quot;:&quot;</literal>.
+
</para>
+
</listitem>
+
</varlistentry>
+
<varlistentry>
+
<term>
+
<literal>types.strMatching</literal>
+
</term>
+
<listitem>
+
<para>
+
A string matching a specific regular expression. Multiple
+
definitions cannot be merged. The regular expression is
+
processed using <literal>builtins.match</literal>.
+
</para>
+
</listitem>
+
</varlistentry>
+
</variablelist>
+
</section>
</section>
-
<section xml:id="sec-option-types-value">
-
<title>Value Types</title>
+
<section xml:id="sec-option-types-submodule">
+
<title>Submodule types</title>
<para>
-
Value types are types that take a value parameter.
+
Submodules are detailed in
+
<link linkend="section-option-types-submodule">Submodule</link>.
</para>
<variablelist>
<varlistentry>
<term>
-
<literal>types.enum</literal>
-
<emphasis><literal>l</literal></emphasis>
-
</term>
-
<listitem>
-
<para>
-
One element of the list
-
<emphasis><literal>l</literal></emphasis>, e.g.
-
<literal>types.enum [ &quot;left&quot; &quot;right&quot; ]</literal>.
-
Multiple definitions cannot be merged.
-
</para>
-
</listitem>
-
</varlistentry>
-
<varlistentry>
-
<term>
-
<literal>types.separatedString</literal>
-
<emphasis><literal>sep</literal></emphasis>
-
</term>
-
<listitem>
-
<para>
-
A string with a custom separator
-
<emphasis><literal>sep</literal></emphasis>, e.g.
-
<literal>types.separatedString &quot;|&quot;</literal>.
-
</para>
-
</listitem>
-
</varlistentry>
-
<varlistentry>
-
<term>
-
<literal>types.ints.between</literal>
-
<emphasis><literal>lowest highest</literal></emphasis>
-
</term>
-
<listitem>
-
<para>
-
An integer between
-
<emphasis><literal>lowest</literal></emphasis> and
-
<emphasis><literal>highest</literal></emphasis> (both
-
inclusive). Useful for creating types like
-
<literal>types.port</literal>.
-
</para>
-
</listitem>
-
</varlistentry>
-
<varlistentry>
-
<term>
<literal>types.submodule</literal>
<emphasis><literal>o</literal></emphasis>
</term>
···
in composed types to create modular options. This is
equivalent to
<literal>types.submoduleWith { modules = toList o; shorthandOnlyDefinesConfig = true; }</literal>.
-
Submodules are detailed in
-
<link linkend="section-option-types-submodule">Submodule</link>.
</para>
</listitem>
</varlistentry>
···
</variablelist>
</section>
<section xml:id="sec-option-types-composed">
-
<title>Composed Types</title>
+
<title>Composed types</title>
<para>
Composed types are types that take a type as parameter.
<literal>listOf int</literal> and
···
</variablelist>
</section>
<section xml:id="sec-option-types-custom">
-
<title>Custom Types</title>
+
<title>Custom types</title>
<para>
Custom types can be created with the
<literal>mkOptionType</literal> function. As type creation
+5 -14
nixos/modules/services/x11/picom.nix
···
addCheck (listOf x) (y: length y == 2)
// { description = "pair of ${x.description}"; };
-
floatBetween = a: b: with types;
-
let
-
# toString prints floats with hardcoded high precision
-
floatToString = f: builtins.toJSON f;
-
in
-
addCheck float (x: x <= b && x >= a)
-
// { description = "a floating point number in " +
-
"range [${floatToString a}, ${floatToString b}]"; };
-
mkDefaultAttrs = mapAttrs (n: v: mkDefault v);
# Basically a tinkered lib.generators.mkKeyValueDefault
···
};
fadeSteps = mkOption {
-
type = pairOf (floatBetween 0.01 1);
+
type = pairOf (types.numbers.between 0.01 1);
default = [ 0.028 0.03 ];
example = [ 0.04 0.04 ];
description = lib.mdDoc ''
···
};
shadowOpacity = mkOption {
-
type = floatBetween 0 1;
+
type = types.numbers.between 0 1;
default = 0.75;
example = 0.8;
description = lib.mdDoc ''
···
};
activeOpacity = mkOption {
-
type = floatBetween 0 1;
+
type = types.numbers.between 0 1;
default = 1.0;
example = 0.8;
description = lib.mdDoc ''
···
};
inactiveOpacity = mkOption {
-
type = floatBetween 0.1 1;
+
type = types.numbers.between 0.1 1;
default = 1.0;
example = 0.8;
description = lib.mdDoc ''
···
};
menuOpacity = mkOption {
-
type = floatBetween 0 1;
+
type = types.numbers.between 0 1;
default = 1.0;
example = 0.8;
description = lib.mdDoc ''