Skip to content

feat: support chained member check fallbacks (a ? b ? c)#433

Open
dyegoaurelio wants to merge 2 commits into
masterfrom
chained-fallbacks
Open

feat: support chained member check fallbacks (a ? b ? c)#433
dyegoaurelio wants to merge 2 commits into
masterfrom
chained-fallbacks

Conversation

@dyegoaurelio

Copy link
Copy Markdown
Contributor

Allow the ? operator to chain: a member check can now be followed by further ? selector.path fallbacks, e.g. {} ? foo ? bar ? null. Previously the operator accepted a single selector path and a chain was a parse error.

Note that this syntax is never useful in practice: it parses left-associatively as (a ? b) ? c, so the first check yields a boolean and each further check tests an attribute on that boolean, making any chain of two or more ? evaluate to constant false. It is valid Nix syntax nonetheless, so the formatter handles it for correctness.

MemberCheck now carries a non-empty list of (question mark, selector path) pairs instead of a single pair. A chain stays on one line when it fits; otherwise it breaks before every question mark, indented one level past the subject:

  aaaaaaaaaaaaaaaaaaaaaaaaaa
    ? bbbbbbbbbbbbbbbbbb
    ? cccccccccccccccccc

Like other operators, a comment trailing a question mark is moved onto its own line above it, keeping the question mark attached to its selector path.

closes #359

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

Nixpkgs diff

@dyegoaurelio

Copy link
Copy Markdown
Contributor Author

the nixpkgs diff was an improvement IMO

diff --git a/nixos/modules/hardware/facter/camera/ipu6.nix b/nixos/modules/hardware/facter/camera/ipu6.nix
index 5319d8bf3d49f..799c221888245 100644
--- a/nixos/modules/hardware/facter/camera/ipu6.nix
+++ b/nixos/modules/hardware/facter/camera/ipu6.nix
@@ -66,7 +66,7 @@ let
       in
       bus_type.name == "PCI"
       && devices
-      ? "${vendorHex}:${deviceHex}:${subVendorHex}:${subDeviceHex}/${baseClassHex}-${subClassHex}-${revisionHex}"
+        ? "${vendorHex}:${deviceHex}:${subVendorHex}:${subDeviceHex}/${baseClassHex}-${subClassHex}-${revisionHex}"
     );
 in
 {

the indentation also applies on ? operators without a further chain

@jfly jfly left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks! I haven't read the haskell diff closely (would prefer to do that in person, but it's totally OK if you get a shipit from someone else before then). The test cases look good, though.

I agree the nixpkgs diff is nice.

Comment thread test/diff/directive_edge_cases/in.nix
Comment thread test/diff/pattern/in.nix
@dyegoaurelio
dyegoaurelio force-pushed the chained-fallbacks branch 2 times, most recently from 84ef425 to 6648956 Compare July 17, 2026 15:30
Disabled regions should not be touched and usually
shouldn't affect formatting on enabled regions.

This documents some instances where a change in a
disabled region would cause enabled regions to change.
Allow the ? operator to chain: a member check can now be followed by
further `? selector.path` fallbacks, e.g. `{} ? foo ? bar ? null`.
Previously the operator accepted a single selector path and a chain
was a parse error.

Note that this syntax is never useful in practice: nix parses
left-associatively as `(a ? b) ? c`, so the first check yields a
boolean and each further check tests an attribute on that boolean,
making any chain of two or more ? evaluate to constant false. It is
valid Nix syntax nonetheless, so the formatter handles it for
correctness.

MemberCheck now carries a non-empty list of (question mark, selector
path) pairs instead of a single pair. A chain stays on one line when
it fits; otherwise it breaks before every question mark, indented one
level past the subject:

```nix
  aaaaaaaaaaaaaaaaaaaaaaaaaa
    ? bbbbbbbbbbbbbbbbbb
    ? cccccccccccccccccc
```

Like other operators, a comment trailing a question mark is moved onto
its own line above it, keeping the question mark attached to its
selector path.

@MattSturgeon MattSturgeon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

SGTM, but I think we should be careful about calling ? chains a "fallback", as that is not what they represent even if that may be what users intended.

Comment thread test/diff/pattern/out.nix
_
)

# Chained fallbacks as default values

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
# Chained fallbacks as default values
# Chained presence checks as default values

Terminology nit: to me, a fallback is what the or operator represents. The ? operator does not really have a fallback concept, and when chained it is just checking that the first Boolean contains an attribute.

Therefore I think we can avoid confusion by talking about attr-presence instead of fallbacks.

Comment thread test/diff/pattern/in.nix
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Chained has attribute treated as invalid syntax

3 participants