Overlay is Class

I’ll make this statement.

  • Overlay is anonymous class.
  • Overlay list is inheritance chain of anonymous classes.
  • self reference is used to call virtual methods and virtual getters. It is called this in Java, self in Python.
  • super is reference to parent class. It is called super in Java, base in C#

Is it? Am I missing something?

Details

The overlay list

[
  (self: super: { callPackage = a: b: { _type = "derivation"; override = x: x; }; })
  (self: super: { package1 = super.callPackage ./package1.nix {}; })
  (self: super: { package2 = super.callPackage ./package2.nix {}; })
  (self: super: { package1 = super.package1.override { inherit (self) package2; }; })
]

is translated into following Java code:

class Derivation { ... }

class Overlay1 {
  public Derivation callPackage(.. a, ... b) {
    return new Derivation(...);
  }
}

class Overlay2 extends Overlay1 {
  private Derivation package1 = null;

  public Derivation getPackage1() { 
    if (package1 == null) {
      package1 = super.callPackage(..., ...);
    }
    return package1; 
  }
}

class Overlay3 extends Overlay2 {
  private Derivation package2 = null;

  public Derivation getPackage2() { 
    if (package2 == null) {
      package2 = super.callPackage(..., ...);
    }
    return package2; 
  }
}

class Overlay4 extends Overlay3 {
  private Derivation package1;

  public override Derivation getPackage1() { 
    if (package1 == null) {
      Map<> overrides = new HashMap<String, Derivation>();
      overrides.put("package2", this.getPackage2());
      package1 = new Derivation(super.getPackage1(), overrides, ...);
    }
    return package1; 
  }
}

In Java case, the “overlay chain” is instantiated with new Overlay4(), and for Nix it is simply

overlay_list: fix (self: foldl (acc: f: acc // f self acc) overlay_list) 

Java is superior

Haha, but consider previous example with rows 3 and 4 flipped:

[
  (self: super: { callPackage = a: b: { _type = "derivation"; override = x: x; }; })
  (self: super: { package1 = super.callPackage ./package1.nix {}; })
  (self: super: { package1 = super.package1.override { inherit (self) package2; }; })
  (self: super: { package2 = super.callPackage ./package2.nix {}; })
]

It would be valid Nix overlays (and run well), but Java compiler will warn you about using getPackage2 before it is declared.

Java has to use reflection to overcome this compile-time restriction:

# call yet unknown virtual method
this.getClass().getMethod("getPackage2").invoke(this)

I also have the feeling that class inheritance and Nix overlays are very similar and I like this state-like way to patch nixpkgs.
Reading your post I was wondering if the order of overlays matters any how ? Why using a list if order doesn’t matter ? The fixpoint convergence does not care about the order, am I wrong here ?
In your last example, I do not understand why java is superior. IMHO it can be handy to transparently call variables that will only be declared in the next overlay. It reminds me recursive attrsets on this point.

The order of overlays matters for super argument. This is also the difference with NixOS module system - it doesn’t have super, so order of module imports doesn’t matter.

Regarding superior or not - I think, it is an error to assume that some symbol will be defined in next overlay. It is fine to expect existing item to be overriden in subsequent overlay.

This thing (use before declare) is already implemented for nixpkgs config. The nixpkgs config is empty by default and thus many places first check if config option is defined. There is a PR to invert this logic - define first (with defaults) and never check for definedness (https://github.com/NixOS/nixpkgs/pull/56227/files#diff-0e7fdf554967c4e9631df38ea6f9fe37R58).

I disagree, mainly because overlays are composable. You can’t create a class and provide it to users without specifying its superclass. Overlays allow the user to decide the contents and order of the chain.

1 Like

Right, but this more about notation. For overlays, you can use simple lists and functions for composition, for Java you have to mangle with package imports.

import overlays.5481ba8a76a8334b;

class MyOverlay extends overlays.5481ba8a76a8334b.Super {
...
}

All you have to do now, is to order overlay class files such that package overlays.5481ba8a76a8334b contains class named Super, and do this for whole chain. A bit more work, agree. But we still have all the benefits of type checker. We can also create an “interface” for Nixpkgs, so you can write:

class MyOverlay 
   extends overlays.5481ba8a76a8334b.Super
   implements overlays.bc0ef283f57fd5e4.Nixpkgs {

We just make Nixpkgs an interface with “default” methods (a Java mixin).

Having each overlay inject a random number just so the user can use that random number to define its super seems incredibly jank :stuck_out_tongue: