Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lib: make extensible #38522

Merged
merged 1 commit into from Apr 7, 2018
Merged

lib: make extensible #38522

merged 1 commit into from Apr 7, 2018

Conversation

infinisil
Copy link
Member

This allows the lib fixed point to be extended with

myLib = lib.extend (self: super: {
  foo = "foo";
})

With this it's possible to have the new modified lib attrset available to all
modules when using evalModules

myLib.evalModules {
  modules = [ ({ lib, ... }: {
    options.bar = lib.mkOption {
      default = lib.foo;
    };
  }) ];
}

=>

{ config = { bar = "foo"; ... }; options = ...; }
Motivation for this change

This will be useful for projects like @rycee home-manager that use the module system themselves without the ability to modify nixpkgs/lib directly. Currently home-manager needs to use the prone to infinite recursion lib module that just adds a lib option. Stuff like with config.lib; at the top of the file will give you infinite recursion in general though.

Another instance is @LnL7's nix-darwin which currently imports its lib directly in every file that needs it (which is what home-manager used previously).

@LnL7 @rycee @Profpatsch @edolstra @shlevy

Things done

Other than running a few very basic commands like the one above I haven't tested this very well. But I'm 99% sure this can't break anything and is fully backwards compatible. Will test it more in a bit.

@@ -59,7 +59,7 @@ rec {
};
};

closed = closeModules (modules ++ [ internalModule ]) ({ inherit config options; lib = import ./.; } // specialArgs);
closed = closeModules (modules ++ [ internalModule ]) ({ inherit config options lib; } // specialArgs);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really sure why lib was not taken from the fixed point before here, seems like a logical thing to do and is necessary for getting the module system benefits out of this change.

Copy link
Member

@Profpatsch Profpatsch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure this is the correct way to implement overriding lib.
@edolstra, @nbp and maybe @shlevy and @aszlig should take a look.

lib/default.nix Outdated

# often used, or depending on very little
trivial = callLibs ./trivial.nix;
fixedPoints = callLibs ./fixed-points.nix;
inherit fixedPoints;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d keep the symmetry with other calls in this module.

lib/default.nix Outdated

lib = fixedPoints.makeExtensible (self: let
callLibs = file: import file { lib = self; };
in rec {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation is broken from here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would you format it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use rec. Use with self; instead.

@infinisil
Copy link
Member Author

Changed it to with self;

Copy link
Member

@Ericson2314 Ericson2314 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix that and @Profpatsch's comment on symmetry, and this looks good to me. Thanks!

Sorry to not catch that before.

lib/default.nix Outdated
@@ -5,13 +5,16 @@
*/
let

callLibs = file: import file { inherit lib; };
fixedPoints = import ./fixed-points.nix {};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do inherit (import ./fixed-points.nix {}) makeExtensible!.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah that sounds better

@Ericson2314
Copy link
Member

@Profpatsch with our changes this does look indeed look correct to me.

@infinisil
Copy link
Member Author

Updated

Copy link
Member

@Ericson2314 Ericson2314 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah forgot to remove old inherit.

lib/default.nix Outdated

# often used, or depending on very little
trivial = callLibs ./trivial.nix;
fixedPoints = callLibs ./fixed-points.nix;
inherit fixedPoints;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah this line still needs to go.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I excuse my sluggishness

This allows the lib fixed point to be extended with

  myLib = lib.extend (self: super: {
    foo = "foo";
  })

With this it's possible to have the new modified lib attrset available to all
modules when using evalModules

  myLib.evalModules {
    modules = [ ({ lib, ... }: {
      options.bar = lib.mkOption {
	default = lib.foo;
      };
    }) ];
  }

  => { config = { bar = "foo"; ... }; options = ...; }
@Ericson2314
Copy link
Member

Great! I'll merge after of borg.

@Ericson2314 Ericson2314 merged commit e1dee4e into NixOS:master Apr 7, 2018
@grahamc
Copy link
Member

grahamc commented Apr 7, 2018

I'm not sure lib should be so easy to extend, since it sort of obscures what is in lib, and possible collisions when new functions are added later on. Maybe not a big concern though? It is sort of like monkeypatching new functions in to core libs for other programming langs, a generally frowned upon thing.

@zimbatm
Copy link
Member

zimbatm commented Apr 7, 2018

I was concerned about evaluation time. result: it doesn't affect time nix-env -qa >/dev/null (around 6s on my laptop).

@infinisil
Copy link
Member Author

@grahamc The same could be said for pkgs overlays though, but these are doing just fine with a much larger attribute count. Also this is no problem of nixpkgs, because extending lib is mostly useful outside of it. If you need to extend lib within nixpkgs you can just add the function you need in nixpkgs/lib as it's been done all this time.

@grahamc
Copy link
Member

grahamc commented Apr 7, 2018

I guess I don't see the benefit of making it extensible, but I can see the benefits of keeping it closed -- preferring explicitness in what is contained in the nixpkgs lib. pkgs being an extensible fixed point isn't a good comparison, because the fixed point is required for dependency resolution and replacing packages -- a desirable set of features which I don't think translate in to eval-time library code.

@zimbatm
Copy link
Member

zimbatm commented Apr 7, 2018

@infinisil why can't the overlay have a myoverlay.lib key with their own extensions?

@infinisil
Copy link
Member Author

@grahamc What is the actual benefit of keeping it closed?
@zimbatm In case of modules, the overridden lib will then only be available in pkgs.lib, as opposed to the normal lib argument, which can give the same infinite recursion problems as mentioned initially with config.lib

@shlevy
Copy link
Member

shlevy commented Apr 7, 2018

I'm with @grahamc here. I think we need concrete use cases to add more configuration knobs and increase complexity, and I just don't see why we need this. Note that we already have config.lib in NixOS to allow modules to give each other helper functions...

There is no real analogy with package set extensibility IMO.

@shlevy
Copy link
Member

shlevy commented Apr 7, 2018

Re infinite recursion, we have mkIf etc. already there.

@infinisil
Copy link
Member Author

Well we can undo this of course and discuss further, i didn't expect this to get merged so quickly anyways.

I think it makes sense to be able to extend the base library like this. When we look at the default arguments to a module they are: config, options and lib. config and options are completely customizable on the module level. lib however is fixed in stone, there's no way to change it prior to this PR. And this change seems like the logical step to get this to be possible.

Note that I'm mostly talking about non-nixpkgs uses of the module system. I stumbled upon this when I wanted to use the module system for my emacs config.

@infinisil
Copy link
Member Author

An alternative, while not as elegant, is to add some functionality to the module system that allows you to set your own lib.

Copy link
Member

@nbp nbp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes made in lib/modules.nix sounds fine to me.

However, I will note that unless you are extending the module system you should probably not do that.

Instead you can set the _module.args.hmLib attribute to whatever new set of library function you want to be added. The reason of having a lib attribute is only for getting the module system library functions without the pkgs attribute.

@rycee
Copy link
Member

rycee commented Apr 8, 2018

Since this (apparently) has no negative impact on today's use in NixOS and Nixpkgs I'd say that its an interesting change that might be well worth experimenting with in non-Nixpkgs projects that use the NixOS module system. Of course, I'm personally thinking about Home Manager here.

I'm also liking nbp's idea of adding a new attribute for the library set but would like the ability to test both variants.

I should also add that I'm partial to this change also on purely esthetic grounds; the symmetry it adds is quite nice :-)

@infinisil
Copy link
Member Author

infinisil commented Apr 8, 2018

Also, this change allows the module system itself to be modified/extended, which would've not been possible without forking nixpkgs before. And I think it makes sense to enable users of nixpkgs (Edit: I mean nixpkgs users that can't modify it directly) to do that. And as said by @rycee, I don't see any negative impact on nixpkgs/NixOS with this.

@Profpatsch
Copy link
Member

Profpatsch commented Apr 8, 2018

Instead you can set the _module.args.hmLib attribute to whatever new set of library function you want to be added. The reason of having a lib attribute is only for getting the module system library functions without the pkgs attribute.

Yes, I’d prefer that to making lib extensible.
Adding documentation how to do that might be the better solution here.

@infinisil
Copy link
Member Author

I discussed other options for modifying the lib argument with @nbp on #nixos and the best thing we could come up with is to use evalModules specialArgs argument, see https://gist.github.com/Infinisil/aa97115ccec082ad192088b17b92f011#gistcomment-2427130. While specialArgs overriding normal arguments like config, options and lib doesn't really look like intended behaviour, it's what makes this work. While it's not as nice as the extensible lib, I'd almost be okay with using this instead. I really don't want all modules not in nixpkgs to have to use

{ lib, myLib, ... }:
with lib;
with myLib;

@Profpatsch
Copy link
Member

I really don't want all modules not in nixpkgs to have to use

What’s so bad about that?

@infinisil
Copy link
Member Author

@Profpatsch If I didn't know the internals of nixpkgs and saw that code I'd ask myself why there are two libs and why they couldn't be combined

@nbp
Copy link
Member

nbp commented Apr 9, 2018

One last thing, using specialArgs or an extended lib (independently of how it is extended) will prevent using the home-manager as a sub-module, which I think is desirable for use cases where the home manager is used to configure some NixOS users.

Therefore, we should find a solution for the with problem described in #38522 (comment)

@infinisil
Copy link
Member Author

Now it's been some time since this was (a bit prematurely) merged and while there were a lot of voices against it, I feel like nobody's gonna undo the change themselves. While I'd love to use this new functionality, I'm gonna hold back until I know this is gonna stay.

To get an idea of amount of agreement I'm asking everybody here to vote on this comment with

  • 👍 You like this change and don't want this to be reverted
  • 👎 You dislike this change, nixpkgs is better off without it and it should be reverted.

@oxij
Copy link
Member

oxij commented Apr 18, 2018 via email

@shlevy
Copy link
Member

shlevy commented Apr 18, 2018

Again, there is no real comparison to overlays here. There have been use cases with a strong need to modify or add to the base package set for years before overlays, and they have been served very well since overlays were introduced. On the other hand, I've literally never felt the need to either override or extend lib, nor seen a compelling example of its usefulness.

@oxij
Copy link
Member

oxij commented Apr 18, 2018 via email

@oxij
Copy link
Member

oxij commented Apr 18, 2018 via email

@infinisil infinisil deleted the extensible-lib branch April 18, 2018 20:32
@infinisil
Copy link
Member Author

@oxij Interesting, very good point. Some excerpts from nixpkgs/lib:

{ system, minimal ? false, config ? {} }:
let pkgs = import ../.. { inherit system config; }; in
with pkgs.lib;
with import ../lib/qemu-flags.nix { inherit pkgs; };

, prefix ? []
, lib ? import ../../lib
}:

This is the whole file:

# TODO: remove this file. There is lib.maybeEnv now
name: default:
let value = builtins.getEnv name; in
if value == "" then default else value

pkgs: with pkgs.lib;
rec {

I think all of these could be implemented by extending lib. I'm not 100% sure how to handle the pkgs argument though, since that's not really a thing in nixpkgs/lib.

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/completly-lost-with-errors-rror-attribute-extend-missing-at-nix-store-b7/28160/4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet