From eb72be85415765d1538bdf6e3b06090614f7a6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Na=C3=AFm=20Favier?= Date: Sun, 17 Jul 2022 12:32:28 +0200 Subject: [PATCH 1/4] lib/types: add `number` For numbers that can be ints or floats. --- lib/types.nix | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/lib/types.nix b/lib/types.nix index 354714b28733..76001804e721 100644 --- a/lib/types.nix +++ b/lib/types.nix @@ -226,11 +226,11 @@ rec { }; 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 = @@ -291,10 +291,34 @@ rec { 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 { From 77307fcff86d3e56285b006b3e9204c6b45d90f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Na=C3=AFm=20Favier?= Date: Sun, 17 Jul 2022 12:56:40 +0200 Subject: [PATCH 2/4] nixos/picom: use `types.numbers.between` --- nixos/modules/services/x11/picom.nix | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/nixos/modules/services/x11/picom.nix b/nixos/modules/services/x11/picom.nix index 2eef71f71fcb..85aa11f147b4 100644 --- a/nixos/modules/services/x11/picom.nix +++ b/nixos/modules/services/x11/picom.nix @@ -11,15 +11,6 @@ let 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 @@ -93,7 +84,7 @@ in { }; 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 = '' @@ -133,7 +124,7 @@ in { }; shadowOpacity = mkOption { - type = floatBetween 0 1; + type = types.numbers.between 0 1; default = 0.75; example = 0.8; description = '' @@ -156,7 +147,7 @@ in { }; activeOpacity = mkOption { - type = floatBetween 0 1; + type = types.numbers.between 0 1; default = 1.0; example = 0.8; description = '' @@ -165,7 +156,7 @@ in { }; inactiveOpacity = mkOption { - type = floatBetween 0.1 1; + type = types.numbers.between 0.1 1; default = 1.0; example = 0.8; description = '' @@ -174,7 +165,7 @@ in { }; menuOpacity = mkOption { - type = floatBetween 0 1; + type = types.numbers.between 0 1; default = 1.0; example = 0.8; description = '' From 5480f45f63f75d9340d5d48602f47d04d3625a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Na=C3=AFm=20Favier?= Date: Mon, 25 Jul 2022 11:19:06 +0200 Subject: [PATCH 3/4] nixos/doc/option-types: refactor, document `number`s --- .../development/option-types.section.md | 68 ++- .../development/option-types.section.xml | 420 ++++++++++-------- 2 files changed, 283 insertions(+), 205 deletions(-) diff --git a/nixos/doc/manual/development/option-types.section.md b/nixos/doc/manual/development/option-types.section.md index 9b35e6630144..7864b62605c2 100644 --- a/nixos/doc/manual/development/option-types.section.md +++ b/nixos/doc/manual/development/option-types.section.md @@ -4,7 +4,7 @@ Option types are a way to put constraints on the values a module option 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 @@ -25,6 +25,11 @@ merging is handled. : 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` : A type that accepts any value and recursively merges attribute sets @@ -95,7 +100,7 @@ merging is handled. problems. ::: -Integer-related types: +### Numeric types {#sec-option-types-numeric} `types.int` @@ -118,6 +123,10 @@ Integer-related types: 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). @@ -127,12 +136,39 @@ Integer-related types: : A port number. This type is an alias to `types.ints.u16`. -String-related types: +`types.float` + +: A floating point number. + +`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 @@ -144,7 +180,7 @@ String-related types: `types.envVar` -: A string. Multiple definitions are concatenated with a collon `":"`. +: A string. Multiple definitions are concatenated with a colon `":"`. `types.strMatching` @@ -152,24 +188,9 @@ String-related types: definitions cannot be merged. The regular expression is processed using `builtins.match`. -## Value Types {#sec-option-types-value} +## Submodule types {#sec-option-types-submodule} -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 "|"`. - -`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`* @@ -178,7 +199,6 @@ Value types are types that take a value parameter. 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 } @@ -239,7 +259,7 @@ Value types are types that take a value parameter. 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. @@ -496,7 +516,7 @@ Types are mainly characterized by their `check` and `merge` functions. 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, diff --git a/nixos/doc/manual/from_md/development/option-types.section.xml b/nixos/doc/manual/from_md/development/option-types.section.xml index 929d5302ed41..e207b97a46f8 100644 --- a/nixos/doc/manual/from_md/development/option-types.section.xml +++ b/nixos/doc/manual/from_md/development/option-types.section.xml @@ -6,7 +6,7 @@ in case of multiple value definitions.
- Basic Types + Basic types Basic types are the simplest available types in the module system. Basic types include multiple string types that mainly differ in @@ -49,6 +49,20 @@ + + + types.enum + l + + + + One element of the list + l, e.g. + types.enum [ "left" "right" ]. + Multiple definitions cannot be merged. + + + types.anything @@ -150,186 +164,232 @@ - - Integer-related types: - - - - - types.int - - - - A signed integer. - - - - - - types.ints.{s8, s16, s32} - - - - 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. - −128 to 127 for 8 - bits). - - - - - - types.ints.unsigned - - - - An unsigned integer (that is >= 0). - - - - - - types.ints.{u8, u16, u32} - - - - Unsigned integers with a fixed length (8, 16 or 32 bits). - They go from 0 to 2^n−1 respectively (e.g. - 0 to 255 for 8 bits). - - - - - - types.ints.positive - - - - A positive integer (that is > 0). - - - - - - types.port - - - - A port number. This type is an alias to - types.ints.u16. - - - - - - String-related types: - - - - - types.str - - - - A string. Multiple definitions cannot be merged. - - - - - - types.lines - - - - A string. Multiple definitions are concatenated with a new - line "\n". - - - - - - types.commas - - - - A string. Multiple definitions are concatenated with a comma - ",". - - - - - - types.envVar - - - - A string. Multiple definitions are concatenated with a - collon ":". - - - - - - types.strMatching - - - - A string matching a specific regular expression. Multiple - definitions cannot be merged. The regular expression is - processed using builtins.match. - - - - +
+ Numeric types + + + + types.int + + + + A signed integer. + + + + + + types.ints.{s8, s16, s32} + + + + 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. + −128 to 127 for 8 + bits). + + + + + + types.ints.unsigned + + + + An unsigned integer (that is >= 0). + + + + + + types.ints.{u8, u16, u32} + + + + Unsigned integers with a fixed length (8, 16 or 32 bits). + They go 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). + + + + + + types.port + + + + A port number. This type is an alias to + types.ints.u16. + + + + + + types.float + + + + A floating point number. + + + + + + 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 + + + + 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 "\n". + + + + + + types.commas + + + + A string. Multiple definitions are concatenated with a + comma ",". + + + + + + types.envVar + + + + A string. Multiple definitions are concatenated with a + colon ":". + + + + + + types.strMatching + + + + A string matching a specific regular expression. Multiple + definitions cannot be merged. The regular expression is + processed using builtins.match. + + + + +
-
- Value Types +
+ Submodule types - Value types are types that take a value parameter. + Submodules are detailed in + Submodule. - - - 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 "|". - - - - - - types.ints.between - lowest highest - - - - An integer between - lowest and - highest (both - inclusive). Useful for creating types like - types.port. - - - types.submodule @@ -345,8 +405,6 @@ in composed types to create modular options. This is equivalent to types.submoduleWith { modules = toList o; shorthandOnlyDefinesConfig = true; }. - Submodules are detailed in - Submodule. @@ -467,7 +525,7 @@
- Composed Types + Composed types Composed types are types that take a type as parameter. listOf int and @@ -850,7 +908,7 @@ nixThings = mkOption {
- Custom Types + Custom types Custom types can be created with the mkOptionType function. As type creation From 52bbbaeb09859b02561a1895f0516e094b2f9d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Na=C3=AFm=20Favier?= Date: Fri, 9 Sep 2022 15:39:35 +0200 Subject: [PATCH 4/4] nixos/doc/option-types: add precision loss warning for floats --- nixos/doc/manual/development/option-types.section.md | 5 +++++ .../manual/from_md/development/option-types.section.xml | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/nixos/doc/manual/development/option-types.section.md b/nixos/doc/manual/development/option-types.section.md index 7864b62605c2..40b4d78b250e 100644 --- a/nixos/doc/manual/development/option-types.section.md +++ b/nixos/doc/manual/development/option-types.section.md @@ -140,6 +140,11 @@ merging is handled. : 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 diff --git a/nixos/doc/manual/from_md/development/option-types.section.xml b/nixos/doc/manual/from_md/development/option-types.section.xml index e207b97a46f8..4036bc0ba743 100644 --- a/nixos/doc/manual/from_md/development/option-types.section.xml +++ b/nixos/doc/manual/from_md/development/option-types.section.xml @@ -256,6 +256,15 @@ A floating point number. + + + Converting a floating point number to a string with + toString or toJSON + may result in + precision + loss. + +