Conventions in std

In principle, we all want to be able to read code with local reasoning.

However, these few conventions are pure quality of life and help us to keep our nix code organized.

Nix File Locations

Nix files are imported from either of these two locations, if present, in this order of precedence:

${cellsFrom}/${cell}/${block}.nix
${cellsFrom}/${cell}/${block}/default.nix

Readme File Locations

Readme files are picked up by the TUI in the following places:

${cellsFrom}/${cell}/Readme.md
${cellsFrom}/${cell}/${block}/Readme.md
${cellsFrom}/${cell}/${block}/${target}.md

Cell Block File Arguments

Each Cell Block is a function and expects the following standardized interface for interoperability:

{ inputs, cell }: {}

The inputs argument

The inputs argument holds all the de-systemized flake inputs plus a few special inputs:

{
  inputs = {
    self = {}; # sourceInfo of the current repository
    nixpkgs = {}; # an _instantiated_ nixpkgs
    cells = {}; # the other cells in this repo
  };
}

The cell argument

The cell argument holds all the different Cell Block targets of the current cell. This is the main mechanism by which code organization and separation of concern is enabled.

The deSytemized inputs

All inputs are scoped for the current system, that is derived from the systems input list to std.grow. That means contrary to the usual nix-UX, in most cases, you don’t need to worry about system.

The current system will be “lifted up” one level, while still providing full access to all systems for cross-compilation scenarios.

# inputs.a.packages.${system}
{
  inputs.a.packages.pkg1 = {};
  inputs.a.packages.pkg2 = {};
  /* ... */
  inputs.a.packages.${system}.pkgs1 = {};
  inputs.a.packages.${system}.pkgs2 = {};
  /* ... */
}

Top-level system-scoping of outputs

Contrary to the upstream flake schema, all outputs are system spaced at the top-level. This allows us to uniformly select on the current system and forget about it for most of the time.

Sometimes nix evaluations don’t strictly depend on a particular system, and scoping them seems counter-intuitive. But due to the fact that function calls are memoized, there is never a penalty in actually scoping them. So for the sake of uniformity, we scope them anyways.

The outputs therefore abide by the following “schema”:

{
  ${system}.${cell}.${block}.${target} = {};
}