7

I am having trouble understanding Nix Pill 14. The author provides makeOverridable, and then challenges the user to integrate it with callPackage. makeOverridable and default.nix are provided, as follows, where makeOverridable is in the file lib.nix, and callPackage is in the file default.nix:

# file: lib.nix

rec {
  makeOverridable = f: origArgs:
    let
      origRes = f origArgs;
    in
      origRes // { override = newArgs: makeOverridable f (origArgs // newArgs); };
}
# file: default.nix

let
  nixpkgs = import <nixpkgs> {};
  allPkgs = nixpkgs // pkgs;
  callPackage = path: overrides:
    let f = import path;
    in f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs) // overrides);
  pkgs = with nixpkgs; {
    mkDerivation = import ./autotools.nix nixpkgs;
    hello = callPackage ./hello.nix { };
    graphviz = callPackage ./graphviz.nix { };
    graphvizCore = callPackage ./graphviz.nix { gdSupport = false; };
  };
in pkgs

This is what I have come up with:

# file: default.nix (my implementation)

let
  nixpkgs = import <nixpkgs> {};
  allPkgs = nixpkgs // pkgs;
  callPackage = path: overrides:
    let
      f = import path;
      origRes = f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs) // overrides);
    in
      origRes // { override = newArgs: callPackage f (overrides // newArgs); };
  pkgs = with nixpkgs; {
    mkDerivation = import ./autotools.nix nixpkgs;
    hello = import ./hello.nix {};
    graphviz = import ./graphviz.nix {};
    graphvizCore = graphviz.override { gdSupport = false; };
  };
in pkgs

I think I have a fundamental misunderstanding of what is going on here. Could you please provide the correct implementation and explain what I am doing wrong?

EDIT: I managed to get it to work, however, it is still not recursive.

# file: default.nix

let
  nixpkgs = import <nixpkgs> {};
  allPkgs = nixpkgs // pkgs;
  callPackage = path: overrides:
  let
    f = import path;
    origArgs = f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs) // overrides);
    makeOverridable = { override = newArgs: (origArgs // newArgs); };
  in
    origArgs // makeOverridable;
pkgs = with nixpkgs; rec {
  mkDerivation = import ./autotools.nix nixpkgs;
  hello = callPackage ./hello.nix { };
  graphviz = callPackage ./graphviz.nix { };
  graphvizCore = graphviz.override { gdSupport = false; };
};
in pkgs

EDIT 2:

# file: default.nix

let
  nixpkgs = import <nixpkgs> {};
  allPkgs = nixpkgs // pkgs;
  makeOverridable = f: origArgs:
    let origRes = f origArgs;
    in origRes // { override = newArgs: makeOverridable f (origArgs // newArgs); };
  callPackage1 = path: overrides:
    let f = import path;
    in f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs) // overrides);
  callPackage = makeOverridable callPackage1;
  pkgs = with nixpkgs; {
    mkDerivation = import ./autotools.nix nixpkgs;
    hello = callPackage ./hello.nix { };
    graphviz = callPackage ./graphviz.nix { };
    graphvizCore = graphviz.override { gdSupport = false; };
  };
in pkgs

SOLUTION:

# file: default.nix

let
  nixpkgs = import <nixpkgs> {};
  allPkgs = nixpkgs // pkgs;
  makeOverridable = f: origArgs:
    let origRes = f origArgs;
    in origRes // { override = newArgs: makeOverridable f (origArgs // newArgs); };
  callPackage1 = path: overrides:
    let f = import path;
    in f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs) // overrides);
  callPackage = path: makeOverridable (callPackage1 path);
  pkgs = with nixpkgs; rec {
    mkDerivation = import ./autotools.nix nixpkgs;
    hello = callPackage ./hello.nix { };
    local_graphviz = callPackage ./graphviz.nix { };
    graphvizCore = local_graphviz.override { gdSupport = false; };
    graphvizCore2 = graphvizCore.override { gdSupport = false; };
  };
in pkgs
webdavis
  • 83
  • 1
  • 5
  • Your definition of `makeOverridable` is wrong. The document you linked to gives two definitions, one which works recursively and one which does not. You aren't using either. – David Grayson Dec 28 '19 at 23:13

2 Answers2

3

If makeOverridable is defined properly using one of the definitions in Nix Pills, you can think of it as a higher-order function: it takes a normal function named f, and it changes it into a new function whose results can be overridden.

Assuming you already have a function named callPackage1, you can make an overridable version like this:

rec {
  callPackage1 = ...;
  callPackage = makeOverridable callPackage1;
}

EDIT 1:

Actually, callPackage1 needs a path in order to return the type of function that is expected by makeOverridable (a function that takes a set and returns an object to which we can add the override attribute). So let's try this definition of callPackage:

callPackage = path: makeOverridable (callPackage1 path);
David Grayson
  • 84,103
  • 24
  • 152
  • 189
  • Hi, thank you for helping me with this. When I implement that I get `error: syntax error, unexpected REC`. – webdavis Dec 29 '19 at 12:17
  • Would you mind posting the file where the syntax error is happening, either on gist.github.com or by editing your question above? Is `rec` lowercase, as I wrote it above? Did you fill in the `...` with an appropriate, syntactically-correct definition of a function? – David Grayson Dec 29 '19 at 18:23
  • I added EDIT 2. `rec` is lowercase as you wrote it. I moved makeOverridable from `lib.nix` to `default.nix` (just copied and pasted) and then included `callPackage1`, and `callPackage` in `rec`. – webdavis Dec 29 '19 at 18:40
  • You can't just define an unnamed set in the middle of a `let` block. The Nix language expects you to be defining named variables in a `let` block. Luckily, `let` blocks behave a lot like recursive sets so you can just remove the `rec {` and the matching `};`, basically just moving those definitions into the `let` block. – David Grayson Dec 29 '19 at 18:47
  • I see. I removed the `rec` wrapper (see above) which fixed that error, but now I get `error: value is a function while a set was expected` – webdavis Dec 29 '19 at 19:02
  • OK, see EDIT 1 in my answer. – David Grayson Dec 29 '19 at 22:33
  • That worked! I had to add `rec` to `pkgs` and change `graphviz.override` to `local_graphviz.override` otherwise it would spit out an error from the graphviz from ``. – webdavis Dec 30 '19 at 01:22
  • I'm glad I could help. If my answer is satisfactory can you upvote it and click the checkmark to accept it? – David Grayson Dec 30 '19 at 19:44
  • Done! Thanks again. However, I have less than 15 reputation points so my upvote was recorded but doesn't change the publicly displayed score. – webdavis Dec 30 '19 at 20:07
2

I am working through the same Nix Pills and had trouble with the reader exercise in Pill 14. I found the answers here helpful.

In my solution the argument to makeOverridable is an unnamed function.

let
  nixpkgs = import <nixpkgs> {};
  allPkgs = nixpkgs // pkgs;
  lib = import ./lib.nix;

  # Function to find arguments of function defined in "path"
  callPackage = path: lib.makeOverridable (overrides:
    { result =
    let f = import path;
        in f (
          (builtins.intersectAttrs (builtins.functionArgs f) allPkgs)
          // overrides
        ); }) {};

  pkgs = with nixpkgs; rec {
    mkDerivation = import ./autotools.nix nixpkgs;
    hello = callPackage ./hello.nix;
    graphviz = callPackage ./graphviz.nix;
    graphvizCore = graphviz.override { gdSupport = false; };
  };

in pkgs
pdxedu
  • 21
  • 2