NixOS module decorators

I couldn’t find a forum category for questions about solutions without problems, so I’ll ask here…

Has anyone written and/or used a function which “decorates” NixOS modules? I’m not talking about nixfmt here. More along the lines of function composition.

A NixOS module is a function like: (please correct if I’m wrong)

args@{ lib, config, name, ... } -> result@{ imports, options, config }

There could exist decorator functions which:

  1. Modify args before evaluating the module.
  2. Modify result after evaluating the module.

For example, suppose you had a flake exporting a NixOS module. You wish to give that module access to the flake self variable as an extra argument.

{
  outputs = { self, nixpkgs }: {
    nixosModules.default = nixpkgs.lib.hypothetical.setModuleArgs
      (args: args // { inherit self; })
      ./module.nix;
  };
}

Or maybe you want to translate all option descriptions into Klingon.

{
  outputs = { self, nixpkgs, klingon }: let inherit (nixpkgs) lib; in {
    nixosModules.default = lib.hypothetical.mapModule (result:
      result // {
        options = lib.mapAttrsRecursive
          (path: value:
            if (lib.last path == "description" && lib.isString value)
            then klingon.lib.fromEnglish value
            else value)
          (result.options or {});
      }) ./module.nix;
  };
}

Usefulness aside, are there reasons why such decorator functions would be impossible or infeasible to implement?

This is essentially how the module system works by itself already. Unfortunately most of that is still undocumented. Check recent discussion on the topic.

Here’s an example of modifying module arguments.

Modifying the effect of one module declaration happens somewhat behind the scenes, and the allowed transformations are determined by option types. Modifying the end result of evalModules can be done directly by working on the attribute set. It’s likely not exactly what you’re looking for. But just in case, literally anything you can think of can be achieved in the module system: It’s only syntactic sugar for fixed-point computation on attribute sets, with some type checking and automatic merging of multiple definitions added on top.