Why does buildInputs accept a list of lists, but behaves differently?

For a long time, I’ve used the following:

File: requirements.nix

let
  pkgs = import
    (fetchTarball "https://github.com/NixOS/nixpkgs/archive/65fae659e31.tar.gz")
    { };
  unstable = import
    (fetchTarball "https://github.com/NixOS/nixpkgs/archive/65fae659e31.tar.gz")
    { };

in with import <nixpkgs> {}; {
  inherit pkgs;
  inherit unstable;

  production_dependencies = [
    pkgs.python3
    pkgs.openssl
    pkgs.postgresql
  ];
  development_dependencies = [
  ];
  contributing_dependencies =
    [];

}

And then have a shell.nix file with:

let requirements = import ./requirements.nix;

in with requirements.pkgs;

mkShell {
  buildInputs = [
    requirements.production_dependencies
    requirements.development_dependencies
  ];
  shellHook = ''
    source $(npm bin)/setup_environment.sh
  '';
}

This seems to work. But then for unrelated issues, I need to install the pyscopg python package using pip from source. And it was erroring out with cryptic openssl errors missing. Through a ton of testing, this somehow does work (actually appending the lists):

let requirements = import ../requirements.nix;

in with requirements.pkgs;

mkShell {
  buildInputs = requirements.production_dependencies
    ++ requirements.development_dependencies;
  shellHook = ''
    source $(npm bin)/setup_environment.sh
  '';
}

Notice I’m properly concatenating the lists instead of creating a list of lists.

So… my question:

Why does a list of lists even work? And when it does, what’s causing it to fail with openssl stuff? Really hard to pin down.

Thanks for any help.

lists of lists only work by accidents and due to nix not having real strict type checks. *Inputs are expected to have lists of packages, so you need to concat them.