lib.crossLists is deprecated, use lib.cartesianProductOfSets instead

I’m getting this error when using the crossLists function, but it’s not clear how cartesianProductOfSets is a replacement. Is this deprecation message correct?

Here’s what I’m doing:

nix-repl> region = [ "us-east-1" "us-east-2" "us-west-2" ]
nix-repl> env = [ "dev" "test" "prod" ]
nix-repl> :p lib.lists.crossLists (x: y: "${y}-${x}") [ region env ]
[ "dev-us-east-1" "test-us-east-1" "prod-us-east-1" "dev-us-east-2" "test-us-east-2" "prod-us-east-2" "dev-us-west-2" "test-us-west-2" "prod-us-west-2" ]

Now I can get to the same result with the suggested alternative, but not straight away.

nix-repl> xs = lib.attrsets.cartesianProductOfSets { inherit region env; }

nix-repl> lib.lists.map (x: "${x.env}-${x.region}") xs
[ "dev-us-east-1" "dev-us-east-2" "dev-us-west-2" "test-us-east-1" "test-us-east-2" "test-us-west-2" "prod-us-east-1" "prod-us-east-2" "prod-us-west-2" ]

Is this how it’s supposed to be done? or am I missing something?

$ nix --version
nix (Nix) 2.20.5

I’ve found the PR where this function was deprecated: lib/attrsets: add cartesianProductOfSets function by tfc · Pull Request #110787 · NixOS/nixpkgs · GitHub

@Infinisil sorry to bother 3 years later :smile: , but given your suggestion there, do you recall if this use case was considered? It seems the original scenario was quite different than mine.

There’s a change in that PR that represents my use case actually, and the answer seems to be use map + cartesianProductOfSets as opposed to just the latter.

I can submit a PR to fix the deprecated message if this is the intention, but wondering can we still have a non-deprecated lib.lists.crossLists function implemented on top of those two functions?

Indeed looks like it’s not quite the same. I think your PR is justified, I’ll take a look :slight_smile:

1 Like

When using mapCartesianProduct as a replacement for crossLists it looks like the order of results you get is now alphabetically sorted. crossLists maintained an order based on the order of the input lists.

If you used crossLists to generate something where the order mattered like a list in the config of a UI it seems like the best alternative right now is to inline the deprecated definition to avoid the warning.

It doesn’t sort, though. (Sorting would be strange behavior and wouldn’t make sense performance-wise, anyway.)

nix-repl> :p lib.mapCartesianProduct ({a, b}: "${toString a}.${toString b}") { a = [ 10 4 18 ]; b = [ 7 3 1 ]; } 
[
  "10.7"
  "10.3"
  "10.1"
  "4.7"
  "4.3"
  "4.1"
  "18.7"
  "18.3"
  "18.1"
]

EDIT: corrected function

Ah, so the sense in which it sorts is that it cares about the lexicographic order of the keys picked, so you can match the behavior of crossLists by adjusting those:

nix-repl> :p lib.mapCartesianProduct ({a, b}: "${b}-${a}") {
              a = ["a" "b" "c" ];
              b = ["d" "e" "f"];
          } 
[
  "d-a"
  "e-a"
  "f-a"
  "d-b"
  "e-b"
  "f-b"
  "d-c"
  "e-c"
  "f-c"
]
nix-repl> :p lib.mapCartesianProduct ({a, _b}: "${_b}-${a}") {
              a = ["a" "b" "c" ];
              _b = ["d" "e" "f"];
          } 
[
  "d-a"
  "d-b"
  "d-c"
  "e-a"
  "e-b"
  "e-c"
  "f-a"
  "f-b"
  "f-c"
]

That’s not related to the function, that’s just how sets work in nix. Order of keys is not going to follow the order that you specified, they are essentially sorted.

nix-repl> { a = 7; _b = 8; }
{
  _b = 8;
  a = 7;
}

Though I understand the resulting behavior here can be confusing.

1 Like

It’s related in the sense that the API taking a set in requires you to pick names and they matter, encouraging you to pick arbitrary names that are alphabetically ordered instead of using meaningful names, while before if you’re using crossLists you can use meaningful names for the inputs to the function you pass to it without needing to fudge them to get your desired output order.

Would it make sense to remove the deprecation warning from crossLists since using mapCartesianProduct + arbitrary names to get the desired order feels kinda kludgy?

1 Like

Another workaround could be creating a second set just to pass into the function:

nix-repl> let
            s = {
              a = ["a" "b" "c" ];
              b = ["d" "e" "f"];
            };
          in
          lib.mapCartesianProduct ({x, y}: "${x}-${y}") { x = s.b; y = s.a; }
[
  "d-a"
  "d-b"
  "d-c"
  "e-a"
  "e-b"
  "e-c"
  "f-a"
  "f-b"
  "f-c"
]

I don’t have a strong opinion on deprecation, probably because I don’t have a strong need for the function; I’ll leave that to others.

1 Like

Opened lib/lists: undeprecate crossLists by LunNova · Pull Request #393573 · NixOS/nixpkgs · GitHub