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

Improved makeOverridable #27319

Merged
merged 9 commits into from Sep 28, 2017
Merged

Conversation

ElvishJerricco
Copy link
Contributor

Motivation for this change: As explained in #26561, using haskellPackages.extend breaks haskellPackages.override, meaning there is no way to override the arguments passed to haskellPackages, such as all-cabal-hashes. This PR introduces a much more powerful notion of overriding into makeOverridable so that the solution to this problem can be enjoyed by everything.

@mention-bot
Copy link

@ElvishJerricco, thanks for your PR! By analyzing the history of the files in this pull request, we identified @ttuegel, @peti and @edolstra to be potential reviewers.

@ElvishJerricco ElvishJerricco changed the title Improved make overridable Improved makeOverridable Jul 12, 2017
@ElvishJerricco ElvishJerricco force-pushed the improved-make-overridable branch 2 times, most recently from b98fce8 to c8f7cff Compare July 13, 2017 05:04
Consequently removing several ad-hoc definitions of the same concept.
@ElvishJerricco ElvishJerricco force-pushed the improved-make-overridable branch 2 times, most recently from 7ea5137 to 8b76496 Compare July 13, 2017 20:52
# function in the resulting attribute set. But it's also useful for
# having an internal structure that extensions can see, but the user
# facing code cannot.
makeExtensibleWithInterface = interface: fext: interface
Copy link
Member

Choose a reason for hiding this comment

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

@cstrahan I need to write something longer up another time, but I want to close my PR in lieu of this generalization.

@Ericson2314
Copy link
Member

Ericson2314 commented Jul 14, 2017

@ElvishJerricco Looking good! Some of the complexity of the *scope stuff I think @nbp and @peti want to remove altogether, but that can be done later of course.

I think iself and WithSelf should become output and WithOutput---I don't want to confuse people over iself vs self. Despite what I said earlier in person, I think output might be a clear name than interface--so the function would be like mkOutput or something.

@ElvishJerricco
Copy link
Contributor Author

@Ericson2314 yea that sounds like a mostly better name, although output is generally a term that refers to Nix store outputs.

@@ -7,26 +7,24 @@
, configurationNix ? import ./configuration-nix.nix
}:

self: # Provided by `callPackageWithSelf`
Copy link
Member

Choose a reason for hiding this comment

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

This would be the final output, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea. Since self becomes the scope for all the packages' overrideScope, it needs to support extend, thus it needs to be the final output, not the internal one.

(extends packageSetConfig
(extends compilerConfig
(extends commonConfiguration
(extends nixConfiguration haskellPackages))))) self
Copy link
Member

Choose a reason for hiding this comment

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

That seems like the wrong thing to put there though?

Copy link
Member

Choose a reason for hiding this comment

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

I'll need to think about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are you talking about the self? There's no reason it shouldn't be the final output, and I didn't want to have to pass around two different selfs

# or extension is added.
callPackageWithSelfWith = autoArgs: fn: args:
let f = if builtins.isFunction fn then fn else import fn;
in callPackageCommon (builtins.functionArgs f) autoArgs (iself: args: _: f args iself ) args;
Copy link
Member

Choose a reason for hiding this comment

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

Right I think the last parameter should also be passed in----it is what mk-haskell-packages is supposed to feed into the (extend _ (extend _ ...)) chain.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know if that's true. It definitely needs the final output for the scope, which is sort of expected to be the same thing as the self in the extensions chain.

@cstrahan
Copy link
Contributor

Oh, I was unaware of this work :). I'll look when I'm back at my desk in a couple hours.

@ElvishJerricco
Copy link
Contributor Author

Wait... Is that travis failure a memory leak in the Nix expressions?... Did I do that?

Fixing the `overrideScope` in `haskellpackages`.
@cstrahan
Copy link
Contributor

@ElvishJerricco So, I need a little bit more time to think this over. In the interim, I'd like to point out -- and get your thoughts on -- another solution I played with: #15095 (comment)

There might be something there that you can draw inspiration from.

@cstrahan
Copy link
Contributor

cstrahan commented Jul 15, 2017

Alright, I've looked through this pretty extensively, and it all looks good to me.

I see from your example:

obj.overridePackage (output: self: super: { args = super.args // {b = self.val.a;}; })

It's apparent that overridePackage can serve the purpose of both override and extend, and solves the issue I was trying to address in #15095 (comment) (where I wanted a way to allow for extensions to refer to the package/package-set's original input arguments), at the expense of being a bit more verbose than either. And for completeness, it looks like you could also elide existing attrs in either case, should the need arise, which is nice new feature. I suspect that override and extend will suit 99% of use cases, while overridePackage makes possible that last 1%, so I think the flexibility justifies the interface. Very nice work!

I don't want to bike-shed too much, but I do wonder if we can come up with more intuitive name. I've been pushing for a change in vernacular for a while now, where override would indicate changing input arguments whereas extend allows for modification of a resulting package/-set. I don't know where that leaves us for naming what is essentially both in one, though. Considering that it applies equally well to both individual packages and sets of packages, the "Package" in overridePackage might be a little misleading.

@cstrahan
Copy link
Contributor

Another thing I'm excited about is being able to use extend with mkDerivation. overideDerivation doesn't compose, and overrideAttrs doesn't give you the fixedpoint, so having override+extend+overridePackage is a huge step up.

@ElvishJerricco
Copy link
Contributor Author

@cstrahan Thanks =) I agree about the naming of overridePackage; I think it's definitely the wrong name. I'm just not sure how to encapsulate the idea that it can both override and extend in one name. I guess the easy answer would be overrideAndExtend =P

@nbp
Copy link
Member

nbp commented Sep 15, 2017

As I mentioned before, if this does not belong in Nixpkgs, then this can go in an overlay [1], where you are free to do anything, and where this will not add burden to maintain Nixpkgs.

[1] https://github.com/mozilla/nixpkgs-mozilla/blob/master/lib-overlay.nix

@edolstra
Copy link
Member

I'm very reluctant to merge this because a) it makes a pretty non-trivial change to a core function; b) changes to makeOverridable may have a significant performance / memory impact; c) I have no idea what it does or why; d) I don't have time to review it at the moment. Sorry about that.

Copy link
Member

@peti peti left a comment

Choose a reason for hiding this comment

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

FWIW, I'll provide my (somewhat unhelpful) feedback, too. I haven't had the time to read, understand, and think about these modification in detail. I find it difficult to predict what kind of consequences these changes will have. In the past, we've merged changes to this code to fix problem A (and did), but then problem B emerged, and this chain of events has been going on like that for the last 2 years or so. It seems to me like someone will have to take a step back and think about overriding in Nix one more time, considering the big picture rather than trying to solve just one particular issue we recognize at this very moment. I might actually give it a shot, but it's not an easy problem as you all know.

Anyhow, I won't merge this PR because I don't have the problem that this code wants to solve. I realize some kind of problem exists, but I just don't have it in practice, so I'm not motivated to get into the details of this PR.

At the same time, if someone else desperately wants this merged, then I won't object either.

@shlevy
Copy link
Member

shlevy commented Sep 15, 2017

Can someone point out an actual problem with this code? Something that will break, something that will slow down, something that will make future code harder to reason about, anything?

We have several people who have taken the time to understand this code who think it's a good solution to real-world problems, a few people who have reviewed it who aren't sure it's the best solution but haven't proposed another, and a few people who haven't reviewed it in detail but don't like it anyway. Why shouldn't we merge in this case? git revert is very cheap.

@Ericson2314
Copy link
Member

I should also point out that the code is structured so that each commit stands alone is readable. @ElvishJerricco has specifically done this to make this otherwise complex change decipherable.

@shlevy
Copy link
Member

shlevy commented Sep 15, 2017

BTW, this is oversimplified but the ultimate theoretical justification for this change is the same as the justification for protected class members: We have details we want to encapsulate from consumers of our interface but make available to extenders of it.

@ElvishJerricco
Copy link
Contributor Author

@shlevy Merge conflicts are fixed btw.

@shlevy
Copy link
Member

shlevy commented Sep 21, 2017

@edolstra Please let me know if your objection is a hard "no" to this being merged, otherwise I'll merge soon.

@cstrahan
Copy link
Contributor

To touch on the performance concerns: if we don't have a readily available means of profiling built into Nix, that ought to be a priority for us, rather than punting on design improvements.

@shlevy shlevy merged commit dbd5009 into NixOS:master Sep 28, 2017
@LnL7
Copy link
Member

LnL7 commented Sep 28, 2017

This broke evaluation of pkgs/top-level/release.nix

@shlevy
Copy link
Member

shlevy commented Sep 28, 2017

@LnL7 751d397 , thanks

});
};

in lib.makeExtensibleWithInterface (x: o: interface (addOverrideFuncs x o) o) (output: self: {
Copy link
Member

Choose a reason for hiding this comment

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

attribute 'makeExtensibleWithInterface' missing, at ~/nixpkgs/lib/customisation.nix:84:6

@LnL7
Copy link
Member

LnL7 commented Sep 28, 2017

@shlevy looks good now

@shlevy
Copy link
Member

shlevy commented Sep 28, 2017

Tarball is broken still, fixing...

@shlevy
Copy link
Member

shlevy commented Sep 28, 2017

Done

@peti
Copy link
Member

peti commented Sep 29, 2017

This PR breaks the evaluation of callPackage ./foo {} where foo/default.nix requires a stdenv argument. For example, I have in my ~/.config/nixpkgs/config.nix:

haskellPackages = super.haskellPackages.override {
  overrides = self: super: {
    Cabal-git = self.callPackage /home/simons/src/cabal/Cabal {};
    cabal-install-git = (self.callPackage /home/simons/src/cabal/cabal-install {}).overrideScope (self: super: {
      Cabal = self.Cabal-git;
      hackage-security = pkgs.haskell.lib.dontCheck super.hackage-security;
    });
  };
};

and /home/simons/src/cabal is a recent checkout of git://github.com/haskell/cabal.git. This used to work fine, but now I get the following error:

error: anonymous function at /home/simons/src/cabal/cabal-install/default.nix:1:1 called without required argument ‘stdenv’, at /home/simons/.nix-defexpr/lib/customisation.nix:218:76

git bisect says that e11dbc3 is the first commit to cause that failure.

@shlevy
Copy link
Member

shlevy commented Sep 29, 2017

@ElvishJerricco @Ericson2314 Any idea here?

@edolstra
Copy link
Member

Please revert. This makes an already bad idea (makeOverridable) even more complicated. For example, the full description of makeOverridableWithInterface is:

  # A more powerful version of `makeOverridable` with features similar
  # to `makeExtensibleWithInterface`.

WTF does that mean? What is makeExtensibleWithInterface? It's not like the code is self-explanatory, with all the selfs and supers and functors and dynamic attributes etc.

Regarding performance metrics, we have those, and they show a significant spike starting today: https://hydra.nixos.org/job/nixpkgs/trunk/metrics#tabs-charts. E.g. nix-env.qa.allocations went from 374 MB to 631 MB.

The claim that reverts are cheap is false in my experience; once changes like these are in and other commits start building on them, it quickly becomes very hard to untangle them.

@shlevy
Copy link
Member

shlevy commented Sep 29, 2017

Reverting

@shlevy
Copy link
Member

shlevy commented Sep 29, 2017

Done

makefu pushed a commit to makefu/nixpkgs that referenced this pull request Sep 29, 2017
makefu pushed a commit to makefu/nixpkgs that referenced this pull request Sep 29, 2017
Reverting NixOS#27319

This reverts commit 9ce7175.
makefu pushed a commit to makefu/nixpkgs that referenced this pull request Sep 29, 2017
Reverting NixOS#27319

This reverts commit 751d397.
@ElvishJerricco
Copy link
Contributor Author

Fair. I understand that this change was complicated. But this still leaves us with issues like #26561. extend is supposed to be a fundamental way to mess with haskellPackages, but it's just broken. What is the correct solution for that? How else do we solve all the various problems outlined in this discussion?

@peti
Copy link
Member

peti commented Sep 29, 2017

The existence of an overrideScope attribute that creates a complete, modified copy of the package set for the context of the given attribute may have been a mistake in the first place. One might very well argue that this method should exists only for the package set, but not for every package inside of it. It's my understanding that this interpretation is also in-line with the way overlays work.

Anyhow, technical discussion of this issue probably belongs into an RFC, not into a pull request -- unless someone feels like spending a lot of effort creating a PR that (a) works, (b) does not affect performance nor memory requirements, (c) is well documented, and (d) works out of the box without breaking evaluation of existing code.

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

Successfully merging this pull request may close these issues.

None yet

9 participants