Is the structure of an overlay too permissive?

The current structure of an overlay is a function which takes two arguments and returns a package set. This way, the attributes in the overlay can depend on the package set which it is applied to. We can imagine some very complicated examples but the below is a simple one.

sel: sup:
if sel.stdenv.isDarwin then { darwin-package = ... } else { }

Depending on the value of isDarwin, either the overlay contains a single package or no packages at all.

Is this intentional? It seems that all the uses of overlays I have seen define a fixed set of attributes.

An alternative design which enforces this property of overlays is that an overlay is a set of functions. Then the attributes an overlay provides could be easily queried without having to do any evaluation. Each attribute will be a function sel: sup: ... but now the passed package set can only affect the definition rather than the overall set.

{
 my-package = sel: sup: ......
}

Cheers,

Matt

2 Likes

I believe self (sel in your example) refers to the complete nixpkgs set after applying previous overlay functions, as attrs like self.callPackage still evaluate in overlays.

It would certainly be nice to have the overlay attrset queryable independently, although I can’t think of a use case at the moment.

FWIW, I don’t believe nixpkgs itself has a mechanism to enforce a fixed attrset, although that is an existing convention. Is it fixed in any case? For example, does nixpkgs evaluate to the same set on all systems?

It seems that being able to query an overlay is a good motivation in the first place. It might be desirable to warn if two overlays try to define the same attribute for example.

It isn’t but it seems to be a reasonable goal prima facie.

Saying this, I did however encounter a recent example where glibcLocales is null on certain systems where it does not make sense rather than not being defined at all. The other approach is taken for some darwin specific attributes. If a package depends on them then the user has to program defensively and give a default argument if the variable is not defined. I don’t think there is any clear guidance about how to handle these cases.