Assosiativity of none for certain operators

What does it mean that the associativity of certain operators are none in the nix manual? Why is the associativity not defined for all operators? Does it mean that the associativity is picked by the interpreter depending on other context? I started thinking about this when I read these lines in nixpkgs nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix at 0de86059128947b2438995450f2c2ca08cc783d5 · NixOS/nixpkgs · GitHub

In this case of chained implication, the final value is actually dependent on the associativity of the imlication. For example
false -> (true -> false) is true whereas (false -> true) -> false is false.

Typically, when an operator does not have defined notational associativity, a programming language will require explicit parentheses and will not parse the code without them. This does appear to happen in Nix with comparison operators:

nix-repl> 1 < 2 < 3                            
error: syntax error, unexpected '<'

       at «string»:1:7:

            1| 1 < 2 <
             |       ^

nix-repl> true == true == true
error: syntax error, unexpected EQ

       at «string»:1:14:

            1| true == true ==
             |              ^

…

But the logical implication operator appears to be right-associative so I guess the operator section is wrong:

$ for a in true false; do for b in true false; do for c in true false; do nix-instantiate --eval --expr "$a -> $b -> $c"; done; done; done
true
false
true
true
true
true
true
true
$ for a in true false; do for b in true false; do for c in true false; do nix-instantiate --eval --expr "$a -> ($b -> $c)"; done; done; done
true
false
true
true
true
true
true
true

As for other operators listed as without associativity, the concept makes little sense for unary operators like ! or - (even though the latter also doubles as a binary operator).

? operator/has attribute could be considered a left-associative operation but it is not an operation in true sense since the right operand cannot be an arbitrary expression.

nix-repl> { a = 1; } ? a ? b
false

nix-repl> ({ a = 1; } ? a) ? b
false

nix-repl> { a = 1; } ? (a ? b)  
error: syntax error, unexpected '(', expecting ID or OR_KW or DOLLAR_CURLY or '"'

       at «string»:1:14:

            1| { a = 1; } ? (
             |              ^

nix-repl> { a = {b = 1;}; } ? a.b
true

Attribute selection is the most interesting one, since it be considered a left-associative binary operation in some sense (with the same caveat as ? operator) but it also has a ternary variant which behaves right associative-ish-ly because the or expression will extend to the end:

nix-repl> let a = {b = {c = 1;};}; in a.b.c   
1

nix-repl> let a = {b = {c = 1;};}; in (a.b).c
1

nix-repl> let a = {b = {c = 1;};}; in a.(b.c) 
error: syntax error, unexpected '(', expecting ID or OR_KW or DOLLAR_CURLY or '"'

       at «string»:1:31:

            1| let a = {b = {c = 1;};}; in a.(
             |                               ^


nix-repl> let a = {bx = {c = {d = 3;};};}; in a.b or {c = {d = 4;};}.c or {d = 5;}.d   
{ d = 4; }

nix-repl> let a = {bx = {c = {d = 3;};};}; in a.b or ({c = {d = 4;};}.c or {d = 5;}.d) 
{ d = 4; }

nix-repl> let a = {bx = {c = {d = 3;};};}; in a.b or ({c = {d = 4;};}.c or {d = 5;}).d
4
5 Likes

So is it a bug that should be reported to the nix repository? Is the best solution to fix the nix interpreter such that the chained implication causes a syntax error? Or should just the documentation be updated to say that implication is right-asociative? Although, can it really be concluded through a simple test that implication is always right associative? I am thinking of all the possible ways expressions can be lazily evaluation in nix?

Changing the parser at this point would probably break too much so updating the docs is the best option.

You are right that a simple test like that cannot prove that the operator is always parsed right-associatively. I am making further assumptions about the interpreter being somewhat well-behaved:

  • The parser parses operators consistently.
  • There are no transformations that would e.g. shuffle the AST nodes around in a way that breaks associativity.
  • The evaluation processes the AST naturally.

If any of these assumptions were false, it would likely lead to noticeable correctness issues so I can reach the conclusion with reasonable certainty. But if you want to be sure, you will need to study the code.