diff --git a/lib/attrsets.nix b/lib/attrsets.nix index 49d311ed37b39..83f8d0f34186e 100644 --- a/lib/attrsets.nix +++ b/lib/attrsets.nix @@ -5,7 +5,7 @@ let inherit (builtins) head length; - inherit (lib.trivial) mergeAttrs warn; + inherit (lib.trivial) isInOldestRelease mergeAttrs warn warnIf; inherit (lib.strings) concatStringsSep concatMapStringsSep escapeNixIdentifier sanitizeDerivationName; inherit (lib.lists) foldr foldl' concatMap elemAt all partition groupBy take foldl; in @@ -885,15 +885,15 @@ rec { # Type ``` - cartesianProductOfSets :: AttrSet -> [AttrSet] + cartesianProduct :: AttrSet -> [AttrSet] ``` # Examples :::{.example} - ## `lib.attrsets.cartesianProductOfSets` usage example + ## `lib.attrsets.cartesianProduct` usage example ```nix - cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; } + cartesianProduct { a = [ 1 2 ]; b = [ 10 20 ]; } => [ { a = 1; b = 10; } { a = 1; b = 20; } @@ -904,7 +904,7 @@ rec { ::: */ - cartesianProductOfSets = + cartesianProduct = attrsOfLists: foldl' (listOfAttrs: attrName: concatMap (attrs: @@ -913,6 +913,40 @@ rec { ) [{}] (attrNames attrsOfLists); + /** + Return the result of function f applied to the cartesian product of attribute set value combinations. + Equivalent to using cartesianProduct followed by map. + + # Inputs + + `f` + + : A function, given an attribute set, it returns a new value. + + `attrsOfLists` + + : Attribute set with attributes that are lists of values + + # Type + + ``` + mapCartesianProduct :: (AttrSet -> a) -> AttrSet -> [a] + ``` + + # Examples + :::{.example} + ## `lib.attrsets.mapCartesianProduct` usage example + + ```nix + mapCartesianProduct ({a, b}: "${a}-${b}") { a = [ "1" "2" ]; b = [ "3" "4" ]; } + => [ "1-3" "1-4" "2-3" "2-4" ] + ``` + + ::: + + */ + mapCartesianProduct = f: attrsOfLists: map f (cartesianProduct attrsOfLists); + /** Utility function that creates a `{name, value}` pair as expected by `builtins.listToAttrs`. @@ -1999,4 +2033,8 @@ rec { # DEPRECATED zip = warn "lib.zip is a deprecated alias of lib.zipAttrsWith." zipAttrsWith; + + # DEPRECATED + cartesianProductOfSets = warnIf (isInOldestRelease 2405) + "lib.cartesianProductOfSets is a deprecated alias of lib.cartesianProduct." cartesianProduct; } diff --git a/lib/default.nix b/lib/default.nix index c77ce88cc4403..44a2a5216ec71 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -86,8 +86,8 @@ let zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil recursiveUpdate matchAttrs mergeAttrsList overrideExisting showAttrPath getOutput getBin getLib getDev getMan chooseDevOutputs zipWithNames zip - recurseIntoAttrs dontRecurseIntoAttrs cartesianProductOfSets - updateManyAttrsByPath; + recurseIntoAttrs dontRecurseIntoAttrs cartesianProduct cartesianProductOfSets + mapCartesianProduct updateManyAttrsByPath; inherit (self.lists) singleton forEach foldr fold foldl foldl' imap0 imap1 concatMap flatten remove findSingle findFirst any all count optional optionals toList range replicate partition zipListsWith zipLists diff --git a/lib/lists.nix b/lib/lists.nix index c162f921280d2..28fa277b22b1f 100644 --- a/lib/lists.nix +++ b/lib/lists.nix @@ -1688,16 +1688,32 @@ rec { ## `lib.lists.crossLists` usage example ```nix - crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]] + crossLists (x: y: "${toString x}${toString y}") [[1 2] [3 4]] => [ "13" "14" "23" "24" ] ``` + The following function call is equivalent to the one deprecated above: + + ```nix + mapCartesianProduct (x: "${toString x.a}${toString x.b}") { a = [1 2]; b = [3 4]; } + => [ "13" "14" "23" "24" ] + ``` ::: */ crossLists = warn - "lib.crossLists is deprecated, use lib.cartesianProductOfSets instead." - (f: foldl (fs: args: concatMap (f: map f args) fs) [f]); + ''lib.crossLists is deprecated, use lib.mapCartesianProduct instead. + For example, the following function call: + + nix-repl> lib.crossLists (x: y: x+y) [[1 2] [3 4]] + [ 4 5 5 6 ] + + Can now be replaced by the following one: + + nix-repl> lib.mapCartesianProduct ({x,y}: x+y) { x = [1 2]; y = [3 4]; } + [ 4 5 5 6 ] + '' + (f: foldl (fs: args: concatMap (f: map f args) fs) [f]); /** Remove duplicate elements from the `list`. O(n^2) complexity. diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix index accceb4ddf9c1..cf4a185c14689 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -33,7 +33,7 @@ let boolToString callPackagesWith callPackageWith - cartesianProductOfSets + cartesianProduct cli composeExtensions composeManyExtensions @@ -71,10 +71,10 @@ let makeIncludePath makeOverridable mapAttrs + mapCartesianProduct matchAttrs mergeAttrs meta - mkOption mod nameValuePair optionalDrvAttr @@ -117,7 +117,6 @@ let expr = (builtins.tryEval expr).success; expected = true; }; - testingDeepThrow = expr: testingThrow (builtins.deepSeq expr expr); testSanitizeDerivationName = { name, expected }: let @@ -1415,7 +1414,7 @@ runTests { }; testToPrettyMultiline = { - expr = mapAttrs (const (generators.toPretty { })) rec { + expr = mapAttrs (const (generators.toPretty { })) { list = [ 3 4 [ false ] ]; attrs = { foo = null; bar.foo = "baz"; }; newlinestring = "\n"; @@ -1429,7 +1428,7 @@ runTests { there test''; }; - expected = rec { + expected = { list = '' [ 3 @@ -1467,13 +1466,10 @@ runTests { expected = "«foo»"; }; - testToPlist = - let - deriv = derivation { name = "test"; builder = "/bin/sh"; system = "aarch64-linux"; }; - in { + testToPlist = { expr = mapAttrs (const (generators.toPlist { })) { value = { - nested.values = rec { + nested.values = { int = 42; float = 0.1337; bool = true; @@ -1686,17 +1682,17 @@ runTests { }; testCartesianProductOfEmptySet = { - expr = cartesianProductOfSets {}; + expr = cartesianProduct {}; expected = [ {} ]; }; testCartesianProductOfOneSet = { - expr = cartesianProductOfSets { a = [ 1 2 3 ]; }; + expr = cartesianProduct { a = [ 1 2 3 ]; }; expected = [ { a = 1; } { a = 2; } { a = 3; } ]; }; testCartesianProductOfTwoSets = { - expr = cartesianProductOfSets { a = [ 1 ]; b = [ 10 20 ]; }; + expr = cartesianProduct { a = [ 1 ]; b = [ 10 20 ]; }; expected = [ { a = 1; b = 10; } { a = 1; b = 20; } @@ -1704,12 +1700,12 @@ runTests { }; testCartesianProductOfTwoSetsWithOneEmpty = { - expr = cartesianProductOfSets { a = [ ]; b = [ 10 20 ]; }; + expr = cartesianProduct { a = [ ]; b = [ 10 20 ]; }; expected = [ ]; }; testCartesianProductOfThreeSets = { - expr = cartesianProductOfSets { + expr = cartesianProduct { a = [ 1 2 3 ]; b = [ 10 20 30 ]; c = [ 100 200 300 ]; @@ -1753,6 +1749,30 @@ runTests { ]; }; + testMapCartesianProductOfOneSet = { + expr = mapCartesianProduct ({a}: a * 2) { a = [ 1 2 3 ]; }; + expected = [ 2 4 6 ]; + }; + + testMapCartesianProductOfTwoSets = { + expr = mapCartesianProduct ({a,b}: a + b) { a = [ 1 ]; b = [ 10 20 ]; }; + expected = [ 11 21 ]; + }; + + testMapCartesianProcutOfTwoSetsWithOneEmpty = { + expr = mapCartesianProduct (x: x.a + x.b) { a = [ ]; b = [ 10 20 ]; }; + expected = [ ]; + }; + + testMapCartesianProductOfThreeSets = { + expr = mapCartesianProduct ({a,b,c}: a + b + c) { + a = [ 1 2 3 ]; + b = [ 10 20 30 ]; + c = [ 100 200 300 ]; + }; + expected = [ 111 211 311 121 221 321 131 231 331 112 212 312 122 222 322 132 232 332 113 213 313 123 223 323 133 233 333 ]; + }; + # The example from the showAttrPath documentation testShowAttrPathExample = { expr = showAttrPath [ "foo" "10" "bar" ]; diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix index 4c62e964c34c4..0f9b712c6df53 100644 --- a/nixos/modules/services/x11/display-managers/default.nix +++ b/nixos/modules/services/x11/display-managers/default.nix @@ -284,7 +284,7 @@ in in # We will generate every possible pair of WM and DM. concatLists ( - builtins.map + lib.mapCartesianProduct ({dm, wm}: let sessionName = "${dm.name}${optionalString (wm.name != "none") ("+" + wm.name)}"; script = xsession dm wm; @@ -312,7 +312,7 @@ in providedSessions = [ sessionName ]; }) ) - (cartesianProductOfSets { dm = dms; wm = wms; }) + { dm = dms; wm = wms; } ); }; diff --git a/nixos/tests/predictable-interface-names.nix b/nixos/tests/predictable-interface-names.nix index 51d5e8ae59b92..9ac4f8211e6b1 100644 --- a/nixos/tests/predictable-interface-names.nix +++ b/nixos/tests/predictable-interface-names.nix @@ -5,7 +5,7 @@ let inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest; - testCombinations = pkgs.lib.cartesianProductOfSets { + testCombinations = pkgs.lib.cartesianProduct { predictable = [true false]; withNetworkd = [true false]; systemdStage1 = [true false]; diff --git a/pkgs/by-name/so/solo5/package.nix b/pkgs/by-name/so/solo5/package.nix index 240c69a40a0f8..57ccd1e24b2bf 100644 --- a/pkgs/by-name/so/solo5/package.nix +++ b/pkgs/by-name/so/solo5/package.nix @@ -63,16 +63,15 @@ in stdenv.mkDerivation { runHook postCheck ''; - meta = { + meta = with lib; { description = "Sandboxed execution environment"; homepage = "https://github.com/solo5/solo5"; - license = lib.licenses.isc; - maintainers = with lib.maintainers; [ ehmry ]; - platforms = builtins.map ({arch, os}: "${arch}-${os}") - (lib.cartesianProductOfSets { - arch = [ "aarch64" "x86_64" ]; - os = [ "freebsd" "genode" "linux" "openbsd" ]; - }); + license = licenses.isc; + maintainers = [ maintainers.ehmry ]; + platforms = mapCartesianProduct ({ arch, os }: "${arch}-${os}") { + arch = [ "aarch64" "x86_64" ]; + os = [ "freebsd" "genode" "linux" "openbsd" ]; + }; }; } diff --git a/pkgs/data/fonts/junicode/tests.nix b/pkgs/data/fonts/junicode/tests.nix index fda7de31670e1..831e60796d12f 100644 --- a/pkgs/data/fonts/junicode/tests.nix +++ b/pkgs/data/fonts/junicode/tests.nix @@ -15,14 +15,13 @@ let ''); in builtins.listToAttrs ( - map - texTest - (lib.attrsets.cartesianProductOfSets { + lib.mapCartesianProduct texTest + { tex = [ "xelatex" "lualatex" ]; fonttype = [ "ttf" "otf" ]; package = [ "junicode" ]; file = [ ./test.tex ]; - }) + } ++ [ (texTest { diff --git a/pkgs/data/icons/catppuccin-cursors/default.nix b/pkgs/data/icons/catppuccin-cursors/default.nix index 20e4718515e61..eeb9dd3227f07 100644 --- a/pkgs/data/icons/catppuccin-cursors/default.nix +++ b/pkgs/data/icons/catppuccin-cursors/default.nix @@ -9,9 +9,8 @@ let palette = [ "Frappe" "Latte" "Macchiato" "Mocha" ]; color = [ "Blue" "Dark" "Flamingo" "Green" "Lavender" "Light" "Maroon" "Mauve" "Peach" "Pink" "Red" "Rosewater" "Sapphire" "Sky" "Teal" "Yellow" ]; }; - product = lib.attrsets.cartesianProductOfSets dimensions; variantName = { palette, color }: (lib.strings.toLower palette) + color; - variants = map variantName product; + variants = lib.mapCartesianProduct variantName dimensions; in stdenvNoCC.mkDerivation rec { pname = "catppuccin-cursors"; diff --git a/pkgs/data/icons/comixcursors/default.nix b/pkgs/data/icons/comixcursors/default.nix index 1c4fdc195180b..735ff686b49cf 100644 --- a/pkgs/data/icons/comixcursors/default.nix +++ b/pkgs/data/icons/comixcursors/default.nix @@ -7,14 +7,13 @@ let thickness = [ "" "Slim_" ]; # Thick or slim edges. handedness = [ "" "LH_" ]; # Right- or left-handed. }; - product = lib.cartesianProductOfSets dimensions; variantName = { color, opacity, thickness, handedness }: "${handedness}${opacity}${thickness}${color}"; variants = # (The order of this list is already good looking enough to show in the # meta.longDescription.) - map variantName product; + lib.mapCartesianProduct variantName dimensions; in stdenvNoCC.mkDerivation rec { pname = "comixcursors"; diff --git a/pkgs/development/ocaml-modules/ocaml-freestanding/default.nix b/pkgs/development/ocaml-modules/ocaml-freestanding/default.nix index 85b741dbc1996..e3627431d9593 100644 --- a/pkgs/development/ocaml-modules/ocaml-freestanding/default.nix +++ b/pkgs/development/ocaml-modules/ocaml-freestanding/default.nix @@ -70,7 +70,7 @@ stdenv.mkDerivation rec { maintainers = [ maintainers.sternenseemann ]; homepage = "https://github.com/mirage/ocaml-freestanding"; platforms = builtins.map ({ arch, os }: "${arch}-${os}") - (cartesianProductOfSets { + (cartesianProduct { arch = [ "aarch64" "x86_64" ]; os = [ "linux" ]; } ++ [