How to use builtins.break effectively

Naively, builtins.break is not very easy to use. Because of laziness, writing break a often will not expose the value of a, or any of the things you might want to look at that are in scope where you put the call to break. This happens because break a gets triggered only when when it actually needs to be evaluated, which often times is inside the body of some function, not in the scope you actually wanted to inspect. Recently, I have discovered a trick that makes debugging so much nicer:

Instead of using

break <expr>

use

(_: break _) <expr>

(the _ is an arbitrary argument name and can be replaced with whatever you like)

Because Nix uses closures, this little lambda exposes everything that was in scope in the place it was defined, no matter where it’s being evaluated, and _ will give you the value of <expr>.

7 Likes

Can you show an example of how this helps? As I understood it, this should be quite literally the exact same thing as far as break goes.

{ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";

  outputs = { nixpkgs, ... }@inputs:
    with builtins;
    let pkgs = nixpkgs.legacyPackages.x86_64-linux; in
    { packages.x86_64-linux.a =
        let a = 1; in
        pkgs.writeText "foo" (break "bar");

      packages.x86_64-linux.b =
        let a = 1; in
        pkgs.writeText "foo" ((_: break _) "bar");
    };
}
$ nix build .#a --debugger
info: breakpoint reached

      at /nix/store/9669y1hx4b5c7678fkyjghlns14pd7x5-source/flake.nix:8:31:

           7|         let a = 1; in
           8|         pkgs.writeText "foo" (break "bar");
            |                               ^
           9|


Starting REPL to allow you to inspect the current state of the evaluator.

Welcome to Nix 2.11.0. Type :? for help.

nix-repl> :env
Env level 0
static: outputs commonAttrs outputsList outputToAttrListElement

Env level 1
static: drv

Env level 2
static: passthru

Env level 3
static: condition

Env level 4
static: overrideDerivation makeOverridable callPackageWith callPackagesWith extendDerivation hydraJob makeScope makeScopeWithSplicing

Env level 5
static: lib

Env level 6
builtins true false null fromTOML fetchTree fetchTarball fetchGit fetchMercurial scopedImport import isNull break abort throw derivationStrict placeholder baseNameOf dirOf removeAttrs map toString derivation


nix-repl> error: quit the debugger
(use '--show-trace' to show detailed location information)
$ nix build .#b --debugger
info: breakpoint reached

      at /nix/store/9669y1hx4b5c7678fkyjghlns14pd7x5-source/flake.nix:12:35:

          11|         let a = 1; in
          12|         pkgs.writeText "foo" ((_: break _) "bar");
            |                                   ^
          13|     };


Starting REPL to allow you to inspect the current state of the evaluator.

Welcome to Nix 2.11.0. Type :? for help.

nix-repl> :env
Env level 0
static: _

Env level 1
static: a

Env level 2
static: pkgs

Env level 3
static:

Env level 4
static: inputs nixpkgs

Env level 5
builtins true false null fromTOML fetchTree fetchTarball fetchGit fetchMercurial scopedImport import isNull break abort throw derivationStrict placeholder baseNameOf dirOf removeAttrs map toString derivation
3 Likes