_: versus { ... }:

Understanding the rationale behind the _: module convention

I’ve been thinking about the _: convention for unused module parameters (versus { ... }:), and I’d love to understand more about its history and rationale. While I appreciate the importance of consistent style in the nix ecosystem, I’m finding it difficult to reconcile some aspects of this particular convention.

My Understanding

The convention suggests using _: instead of { ... }: when we’re not using a module’s input parameters. For example:

# Preferred
_: {
  programs.git.enable = true;
}

# Discouraged
{ ... }: {
  programs.git.enable = true;
}

Questions and Observations

Potential Benefits of _:

  1. It’s slightly shorter (2 characters fewer).
  2. It signals “unused parameter” (a pattern seen in some other languages).
  3. It’s currently the recommended style, ensuring consistency (though I can’t find out why, the purpose of this post).

Some Considerations That Give Me Pause

  1. Pattern Consistency

    • Nix uses { ... } extensively for argument definitions throughout the language.
    • Introducing _ creates a special case that breaks this pattern.
    • When reading Nix code, we seem to be encouraged to pattern-match on { } for argument definitions, or lack thereof.
  2. Programming Language Conventions

    • One justification I’ve seen is that _ for unused parameters is familiar from other languages.
    • However, Nix intentionally differs from mainstream languages in many more fundamental ways than this:
      • Space-separated function application (myFunction arg1 arg2).
      • Colon-based lambda syntax (arg1: arg2: arg1 + arg2).
      • let ... in bindings.
      • Attribute sets with dot access.
      • The inherit keyword.
      • Paths as first-class citizens.
    • Given these intentional differences, optimizing for familiarity in this one case seems inconsistent.
  3. Semantic Clarity

    • There seems to be a tradeoff here between two types of explicitness:
      • { ... }: explicitly shows the shape of potential module arguments, maintaining visual consistency with other Nix parameter blocks.
      • _: explicitly indicates “I’m intentionally not using any arguments right now.”
    • This raises an interesting question: in a module system where parameters might be needed later, which form of explicitness is more valuable?
    • Is it better to signal “I’m not using these now” (_:), or to maintain the visual pattern that reminds readers “these are the standard module parameters” ({ ... }:) which I haven’t modified?

Questions I’d like to understand

  1. What’s the historical context for this convention? Who introduced it and what problems was it solving?
  2. Are there technical benefits to _: that I’m missing?
  3. Given Nix’s unique syntax in other areas, why was this particular mainstream convention chosen to enforce? Was it maybe part of a wider set of changes that together solved a meta problem?
  4. Does the code warning about { ... }: over _: provide enough value to justify the noise it adds to rebuild or development?

I want to emphasize that I’m not suggesting a change of anything—I’m genuinely curious about understanding the reasoning behind this convention. Maybe there are compelling benefits I haven’t considered, or interesting historical context that would help it make more sense to me.

Looking forward to learning from the community’s insights!

PS.

  • I have read the Community Guidelines; I believe I comply.
  • I’ve also searched for similar topics. Such discussions as - mkDerivation rec {} versus mkDerivation (finalAttrs: {}) are on the general theme, but no-one seems to have covered this point explicitly.
    I think therefore this post is relevant, but it’s my first post on this forum, so If I’ve failed in complying on either point, it was unintentional … be gentle :wink:

The de-facto preference would be to drop the arg entirely.
Modules can be a function that returns an attrset, or a simple attrset directly if you don’t depend on any module args (config, options, pkgs, lib, etc.)

Also I have seen the { ... }: convention which reads to me like beginner copy-paste, but I have never seen _: in the context of the module argset.
But in other cases, where the argument’s name nor contents matter, because the arg isn’t used, _ is used simply to indicate that we don’t use the arg at all. This convention comes from other functional programming languages, as I understand it.

EDIT: Also I think another point of confusion is what { ... } means.
It means that the argument is an attrset specifically (so other primitive datatypes cannot be passed in) and that we don’t care about the contents of the attrset because we don’t use certain attributes within the attrset that weren’t bound specifically. See Language Constructs - Nix Reference Manual

1 Like