diff options
author | Silvan Mosberger <contact@infinisil.com> | 2021-01-29 20:22:12 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-29 20:22:12 +0100 |
commit | aa48e205a2e0516ff4b96c79eeb666aaaccd418c (patch) | |
tree | 4e8f6542985139c8e498057dcbdbfd6fea0c3648 | |
parent | 417b19df406ae6106d80a6c551787cd13d0299dd (diff) | |
parent | 577d58a8e01aead5eb237d39a6d7df1fb7aa00c4 (diff) |
Merge pull request #110787 from tfc/cartesian-product
lib/attrsets: add cartesianProductOfSets function
-rw-r--r-- | doc/functions/library/attrsets.xml | 39 | ||||
-rw-r--r-- | lib/attrsets.nix | 19 | ||||
-rw-r--r-- | lib/default.nix | 2 | ||||
-rw-r--r-- | lib/lists.nix | 4 | ||||
-rw-r--r-- | lib/tests/misc.nix | 67 | ||||
-rw-r--r-- | nixos/modules/services/x11/display-managers/default.nix | 6 | ||||
-rw-r--r-- | nixos/tests/predictable-interface-names.nix | 8 | ||||
-rw-r--r-- | pkgs/os-specific/solo5/default.nix | 9 |
8 files changed, 142 insertions, 12 deletions
diff --git a/doc/functions/library/attrsets.xml b/doc/functions/library/attrsets.xml index 3c5823c25891..7ef0d16624c8 100644 --- a/doc/functions/library/attrsets.xml +++ b/doc/functions/library/attrsets.xml @@ -1711,4 +1711,43 @@ recursiveUpdate </example> </section> + <section xml:id="function-library-lib.attrsets.cartesianProductOfSets"> + <title><function>lib.attrsets.cartesianProductOfSets</function></title> + + <subtitle><literal>cartesianProductOfSets :: AttrSet -> [ AttrSet ]</literal> + </subtitle> + + <xi:include href="./locations.xml" xpointer="lib.attrsets.cartesianProductOfSets" /> + + <para> + Return the cartesian product of attribute set value combinations. + </para> + + <variablelist> + <varlistentry> + <term> + <varname>set</varname> + </term> + <listitem> + <para> + An attribute set with attributes that carry lists of values. + </para> + </listitem> + </varlistentry> + </variablelist> + + <example xml:id="function-library-lib.attrsets.cartesianProductOfSets-example"> + <title>Creating the cartesian product of a list of attribute values</title> +<programlisting><![CDATA[ +cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; } +=> [ + { a = 1; b = 10; } + { a = 1; b = 20; } + { a = 2; b = 10; } + { a = 2; b = 20; } + ] +]]></programlisting> + </example> + </section> + </section> diff --git a/lib/attrsets.nix b/lib/attrsets.nix index d91d7a0cd47e..0ce3aaeca452 100644 --- a/lib/attrsets.nix +++ b/lib/attrsets.nix @@ -183,6 +183,24 @@ rec { else []; + /* Return the cartesian product of attribute set value combinations. + + Example: + cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; } + => [ + { a = 1; b = 10; } + { a = 1; b = 20; } + { a = 2; b = 10; } + { a = 2; b = 20; } + ] + */ + cartesianProductOfSets = attrsOfLists: + lib.foldl' (listOfAttrs: attrName: + concatMap (attrs: + map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName} + ) listOfAttrs + ) [{}] (attrNames attrsOfLists); + /* Utility function that creates a {name, value} pair as expected by builtins.listToAttrs. @@ -493,5 +511,4 @@ rec { zipWithNames = zipAttrsWithNames; zip = builtins.trace "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith; - } diff --git a/lib/default.nix b/lib/default.nix index 803f1f765647..50320669e280 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -78,7 +78,7 @@ let zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil recursiveUpdate matchAttrs overrideExisting getOutput getBin getLib getDev getMan chooseDevOutputs zipWithNames zip - recurseIntoAttrs dontRecurseIntoAttrs; + recurseIntoAttrs dontRecurseIntoAttrs cartesianProductOfSets; inherit (self.lists) singleton forEach foldr fold foldl foldl' imap0 imap1 concatMap flatten remove findSingle findFirst any all count optional optionals toList range partition zipListsWith zipLists diff --git a/lib/lists.nix b/lib/lists.nix index 06cee2eb112a..56af4d9daa18 100644 --- a/lib/lists.nix +++ b/lib/lists.nix @@ -629,7 +629,9 @@ rec { crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]] => [ "13" "14" "23" "24" ] */ - crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f]; + crossLists = builtins.trace + "lib.crossLists is deprecated, use lib.cartesianProductOfSets instead" + (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 35a5801c724f..0d249968402d 100644 --- a/lib/tests/misc.nix +++ b/lib/tests/misc.nix @@ -660,4 +660,71 @@ runTests { expected = [ [ "foo" ] [ "foo" "<name>" "bar" ] [ "foo" "bar" ] ]; }; + testCartesianProductOfEmptySet = { + expr = cartesianProductOfSets {}; + expected = [ {} ]; + }; + + testCartesianProductOfOneSet = { + expr = cartesianProductOfSets { a = [ 1 2 3 ]; }; + expected = [ { a = 1; } { a = 2; } { a = 3; } ]; + }; + + testCartesianProductOfTwoSets = { + expr = cartesianProductOfSets { a = [ 1 ]; b = [ 10 20 ]; }; + expected = [ + { a = 1; b = 10; } + { a = 1; b = 20; } + ]; + }; + + testCartesianProductOfTwoSetsWithOneEmpty = { + expr = cartesianProductOfSets { a = [ ]; b = [ 10 20 ]; }; + expected = [ ]; + }; + + testCartesianProductOfThreeSets = { + expr = cartesianProductOfSets { + a = [ 1 2 3 ]; + b = [ 10 20 30 ]; + c = [ 100 200 300 ]; + }; + expected = [ + { a = 1; b = 10; c = 100; } + { a = 1; b = 10; c = 200; } + { a = 1; b = 10; c = 300; } + + { a = 1; b = 20; c = 100; } + { a = 1; b = 20; c = 200; } + { a = 1; b = 20; c = 300; } + + { a = 1; b = 30; c = 100; } + { a = 1; b = 30; c = 200; } + { a = 1; b = 30; c = 300; } + + { a = 2; b = 10; c = 100; } + { a = 2; b = 10; c = 200; } + { a = 2; b = 10; c = 300; } + + { a = 2; b = 20; c = 100; } + { a = 2; b = 20; c = 200; } + { a = 2; b = 20; c = 300; } + + { a = 2; b = 30; c = 100; } + { a = 2; b = 30; c = 200; } + { a = 2; b = 30; c = 300; } + + { a = 3; b = 10; c = 100; } + { a = 3; b = 10; c = 200; } + { a = 3; b = 10; c = 300; } + + { a = 3; b = 20; c = 100; } + { a = 3; b = 20; c = 200; } + { a = 3; b = 20; c = 300; } + + { a = 3; b = 30; c = 100; } + { a = 3; b = 30; c = 200; } + { a = 3; b = 30; c = 300; } + ]; + }; } diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix index 6945a241f92f..9fdbe753dad5 100644 --- a/nixos/modules/services/x11/display-managers/default.nix +++ b/nixos/modules/services/x11/display-managers/default.nix @@ -444,8 +444,8 @@ in in # We will generate every possible pair of WM and DM. concatLists ( - crossLists - (dm: wm: let + builtins.map + ({dm, wm}: let sessionName = "${dm.name}${optionalString (wm.name != "none") ("+" + wm.name)}"; script = xsession dm wm; desktopNames = if dm ? desktopNames @@ -472,7 +472,7 @@ in providedSessions = [ sessionName ]; }) ) - [dms wms] + (cartesianProductOfSets { dm = dms; wm = wms; }) ); # Make xsessions and wayland sessions available in XDG_DATA_DIRS diff --git a/nixos/tests/predictable-interface-names.nix b/nixos/tests/predictable-interface-names.nix index bab091d57acf..c0b472638a14 100644 --- a/nixos/tests/predictable-interface-names.nix +++ b/nixos/tests/predictable-interface-names.nix @@ -5,7 +5,11 @@ let inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest; -in pkgs.lib.listToAttrs (pkgs.lib.crossLists (predictable: withNetworkd: { + testCombinations = pkgs.lib.cartesianProductOfSets { + predictable = [true false]; + withNetworkd = [true false]; + }; +in pkgs.lib.listToAttrs (builtins.map ({ predictable, withNetworkd }: { name = pkgs.lib.optionalString (!predictable) "un" + "predictable" + pkgs.lib.optionalString withNetworkd "Networkd"; value = makeTest { @@ -30,4 +34,4 @@ in pkgs.lib.listToAttrs (pkgs.lib.crossLists (predictable: withNetworkd: { machine.${if predictable then "fail" else "succeed"}("ip link show eth0") ''; }; -}) [[true false] [true false]]) +}) testCombinations) diff --git a/pkgs/os-specific/solo5/default.nix b/pkgs/os-specific/solo5/default.nix index 2dbeca98051d..19d1aa3b5ebe 100644 --- a/pkgs/os-specific/solo5/default.nix +++ b/pkgs/os-specific/solo5/default.nix @@ -50,10 +50,11 @@ in stdenv.mkDerivation { homepage = "https://github.com/solo5/solo5"; license = licenses.isc; maintainers = [ maintainers.ehmry ]; - platforms = lib.crossLists (arch: os: "${arch}-${os}") [ - [ "aarch64" "x86_64" ] - [ "freebsd" "genode" "linux" "openbsd" ] - ]; + platforms = builtins.map ({arch, os}: "${arch}-${os}") + (cartesianProductOfSets { + arch = [ "aarch64" "x86_64" ]; + os = [ "freebsd" "genode" "linux" "openbsd" ]; + }); }; } |