Nix Language Quirks: Difference between revisions

imported>Danbst
added x:x example
Some formatting changes and link to a separate quirk list
 
(37 intermediate revisions by 10 users not shown)
Line 3: Line 3:
== <code>with</code> and <code>let</code> ==
== <code>with</code> and <code>let</code> ==


<code>with</code> gets less priority then <code>let</code>. This can lead to confusions, especially if you like to write <code>with pkgs;</code>:
<code>with</code> gets less priority than <code>let</code>. This can lead to confusions, especially if you like to write <code>with pkgs;</code>:


  <nowiki>
  <syntaxHighlight lang=nix>
nix-repl> pkgs = { x = 1; }
nix-repl> pkgs = { x = 1; }


Line 12: Line 12:


nix-repl> with pkgs; let x = 2; in x
nix-repl> with pkgs; let x = 2; in x
2</nowiki>
2</syntaxHighlight>


So we see, that <code>let</code> binding overrides <code>with</code> binding. But what about this?
So we see, that <code>let</code> binding overrides <code>with</code> binding. But what about this?


  <nowiki>
  <syntaxHighlight lang=nix>
nix-repl> let x = 2; in with pkgs; x
nix-repl> let x = 2; in with pkgs; x
2</nowiki>
2 </syntaxHighlight>


Nah, <code>with</code> and <code>let</code> have different priority when resolving names.
In this case, <code>with</code> and <code>let</code> have different priority when resolving names.


[https://github.com/NixOS/nix/issues/1361 Good discussion on this topic]
[https://github.com/NixOS/nix/issues/1361 Good discussion on this topic]
Generally the use of <code>with</code> is discouraged. See the [https://nix.dev/guides/best-practices#with-scopes best practices guide] for how best to use <code>inherit</code> as an alternative.


== Old <code>let</code> syntax ==
== Old <code>let</code> syntax ==


This is an old Nix syntax, that probably isn't used much
This is an [https://github.com/NixOS/nix/issues/1361#issuecomment-323050690 old] Nix syntax, that probably isn't used much
  <nowiki>
  <syntaxHighlight lang=nix>
nix-repl> let { x = 1; y = x + 1; body = y; }
nix-repl> let { x = 1; y = x + 1; body = y; }
2</nowiki>
2 </syntaxHighlight>
It is equivalent to modern syntax expression <code>let x = 1; y = x + 1; in y</code>. Note, that it doesn't require <code>rec</code> keyword.
It is equivalent to modern syntax expression <code>let x = 1; y = x + 1; in y</code>. Note, that it doesn't require <code>rec</code> keyword.


Note, that it isn't equivalent to <code>with rec { x = 1; y = x + 1; body = y; }; body</code> because of mentioned <code>with</code> and <code>let</code> quirk, but is same as <code>rec { x = 1; y = x + 1; body = y; }.body</code>
Note, that it isn't equivalent to <code>with rec { x = 1; y = x + 1; body = y; }; body</code> because of mentioned <code>with</code> and <code>let</code> quirk, but is same as <code>rec { x = 1; y = x + 1; body = y; }.body</code>
== Default values are not bound in <code>@</code> syntax ==
Destructured arguments can have default values, but those default values are part of the full function argument.
In the following example, calling the function that binds a default value <code>"a"</code> to the argument's attribute <code>a</code> with an empty attribute set as an argument will produce an empty attribute set <code>args</code> instead of <code>{ a = "a"; }</code>:
<syntaxHighlight lang=nix>
(args@{a ? "a"}: args) {}
</syntaxHighlight>
<code>{ }</code>
Related: [https://github.com/NixOS/nix/issues/1461 GitHub issue filed 2017]


== Something that looks like both record attribute and <code>let</code>-binding ==
== Something that looks like both record attribute and <code>let</code>-binding ==
Destructuring function argument - is a great feature of Nix.
Destructuring function argument - is a great feature of Nix.
<nowiki>
  <syntaxHighlight lang=nix>
nix-repl> f = { x ? 1, y ? 2 }: x + y
nix-repl> f = { x ? 1, y ? 2 }: x + y


nix-repl> f { }
nix-repl> f { }
3</nowiki>
3 </syntaxHighlight>


The fact that we can add <code>@args</code> argument assignment is also cool
The fact that we can add <code>@args</code> argument assignment is also cool
  <nowiki>
  <syntaxHighlight lang=nix>
nix-repl> f = { x ? 1, y ? 2, ... }@args: with args; x + y + z
nix-repl> f = { x ? 1, y ? 2, ... }@args: with args; x + y + z


nix-repl> f { z = 3; }
nix-repl> f { z = 3; }
6</nowiki>
6 </syntaxHighlight>


But don't be fooled, <code>args</code> doesn't necessarily contain <code>x</code> and <code>y</code>:
But don't be fooled, <code>args</code> doesn't necessarily contain <code>x</code> and <code>y</code>:
<nowiki>
  <syntaxHighlight lang=nix>
nix-repl> f = { x ? 1, y ? 2, ... }@args: args.x + args.y + args.z
nix-repl> f = { x ? 1, y ? 2, ... }@args: args.x + args.y + args.z


nix-repl> f { z = 3;}
nix-repl> f { z = 3;}
error: attribute ‘x’ missing, at (string):1:30</nowiki>
error: attribute ‘x’ missing, at (string):1:30 </syntaxHighlight>


These <code>x</code> and <code>y</code> are in fact <code>let</code>-bindings, but overridable ones.
These <code>x</code> and <code>y</code> are in fact <code>let</code>-bindings, but overridable ones.


== Imports and namespaces ==
== Imports and namespaces ==
There is a keyword <code>import</code>, but it's equivalent in other languages is <code>eval</code>. It can be used for namespacing too:
Nix includes a keyword <code>import</code>, but it's equivalent in other languages is <code>eval</code>.  
 
It is typically be used for namespacing:


  <nowiki>let
  <syntaxHighlight lang=nix>let
   pkgs = import <nixpkgs> {};
   pkgs = import <nixpkgs> {};
   lib = import <nixpkgs/lib>;
   lib = import <nixpkgs/lib>;
in
in
   pkgs.runCommand (lib.strings.removePrefix "....</nowiki>
   pkgs.runCommand (lib.strings.removePrefix ".... </syntaxHighlight>
 
consider the use of <code>import</code> here similar to using <code>qualified import ...</code> in Haskell or <code>import ...</code> in Python. Another (discouraged and increasingly uncommon) way of importing is [https://nix.dev/manual/nix/2.24/language/syntax#with-expressions <code>with import ...;</code>], which corresponds to Python <code>from ... import *</code>. This use of <code>with</code> imports everything from the target into scope, which has numerous potential gotchas and problems, and so using [https://nix.dev/guides/best-practices#with-scopes <code>inherit</code>] instead is encouraged and preferred.
 
== <code>builtins.replaceStrings</code> key match on "" ==
 
Syntax:
<syntaxHighlight lang=nix>
builtins.replaceStrings [match] [replace] string
</syntaxHighlight>
 
The [https://noogle.dev/f/builtins/replaceStrings <code>builtins.replaceStrings</code>] function allows matching <code>""</code> in <code>string</code>. <code>[match]</code> gets checked sequentially, and when <code>""</code> is checked - it ''always'' matches. And so - when <code>""</code> is checked it ''always'' inserts the corresponding replacement from <code>[replace]</code>, then the next char in <code>string</code> gets inserted, and then the next char after that from <code>string</code> gets processed.
<syntaxHighlight lang=nix>
nix-repl> builtins.replaceStrings ["" "e"] [" " "i"] "Hello world"
" H e l l o  w o r l d "
nix-repl> builtins.replaceStrings ["ll" ""] [" " "i"] "Hello world"
"iHie ioi iwioirilidi"
</syntaxHighlight>
 
== Indented strings trim leading whitespace ==
 
Leading spaces are removed from both single-line and multi-line <strong>indented strings</strong>.
 
<syntaxHighlight lang=nix>
''  s  '' == "s  "
</syntaxHighlight>
 
Usually, indented strings have multiple lines:
 
<syntaxHighlight lang=nix>
''
  s
'' == "s\n"
</syntaxHighlight>
 
Though note that [https://en.wikipedia.org/wiki/Tab_key#Tab_characters tab characters] are ''not'' stripped:
 
<syntaxhighlight lang=nix>
''
s
'' ==  " s\n"
</syntaxhighlight>
 
This is documented in more detail in the [https://nix.dev/manual/nix/2.24/language/syntax#string-literal String section] of the Nix reference manual. Also see [https://github.com/NixOS/nix/issues/7834 NixOS/nix#7834] and [https://github.com/NixOS/nix/pull/9971 NixOS/nix#9971] for more information.
 
== Integer precision ==
 
Integer precision is limited to [https://en.m.wikipedia.org/wiki/64-bit_computing 64-bit] in the original Nix interpreter.
 
So the valid integer range is from -2**63 to 2**63-1 = from -9223372036854775808 to 9223372036854775807
 
Integer overflow is not an error
 
<syntaxHighlight lang=nix>
nix-repl> 9223372036854775807 + 1
-9223372036854775808
</syntaxHighlight>
 
Invalid integer literals throw
 
<syntaxHighlight lang=nix>
nix-repl> 9223372036854775808 
error: invalid integer '9223372036854775808'
</syntaxHighlight>
<!-- TODO Float precision -->
 
== No negative number literals ==
 
Negative numbers are parsed as "zero minus positive"
 
<pre>
nix-instantiate --parse --expr '(-1)'
(__sub 0 1)
</pre>
 
So this throws, because the positive number is out of range
 
<syntaxHighlight lang=nix>
nix-repl> -9223372036854775808
error: invalid integer '9223372036854775808'
</syntaxHighlight>
 
but this works
 
<syntaxHighlight lang=nix>
nix-repl> -9223372036854775807 - 1
-9223372036854775808
</syntaxHighlight>
 
== Attribute set entries with a name that evaluates to null will not be added to the set  ==
 
From [https://nix.dev/manual/nix/2.24/language/syntax#attrs-literal this] section of the Nix Reference Manual:
 
<blockquote>
In the special case where an attribute name inside of a set declaration evaluates to null (which is normally an error, as null cannot be coerced to a string), that attribute is simply not added to the set:
 
{ ${if foo then "bar" else null} = true; }
 
This will evaluate to {} if foo evaluates to false.
</blockquote>
 
The relevant source can be found [https://github.com/NixOS/nix/blob/26c3fc11eada3fa7df0284190095868a947fefe2/src/libexpr/eval.cc#L1249-L1250 here].
 
This feature can be used to conditionally include or exclude attribute set entries, for example:
 
<syntaxHighlight lang=nix>
nix-repl> { ${if true then "foo" else null} = "bar"; }     
{ foo = "bar"; }
 
nix-repl> { ${if false then "foo" else null} = "bar"; }
{ }
</syntaxHighlight>
 
This might be used as an alternative to conditionally merging attribute sets using <code>//</code> like the following:
 
<syntaxHighlight lang=nix>
{ a = "b"; } // (if true then { foo = "bar"; } else { } )
</syntaxHighlight>
 
== Hexadecimal, octal, and binary ==
 
As of late 2024, Nix doesn't contain builtin support for parsing many number formats like hexadecimal, octal, and binary. It ''does'', however, support the [https://noogle.dev/f/builtins/fromTOML <code>builtins.fromTOML</code>] function, which [https://github.com/NixOS/nix/issues/7578#issuecomment-1955985859 can be used] to parse these number formats.
 
<syntaxHighlight lang=nix>
nix-repl> (builtins.fromTOML "octal = 0o11").octal
9
 
nix-repl> (builtins.fromTOML "binary = 0b1001").binary
9
 
nix-repl> (builtins.fromTOML "hex = 0x09").hex       
9
</syntaxHighlight>
 
== Mimicking case statements with attribute sets ==
 
Nix doesn't include native support for case statements, however when dealing with string types it's possible to use some string interpolation behavior to achieve something similar to case statement behavior, as described in this [https://discourse.nixos.org/t/case-statement-expr/27741/12 thread].
 
In the example from the thread, given some string argument <code>x</code>, the following code would place different values into a text file depending on it's value:
 
<syntaxHighlight lang=nix>
environment.etc."just/for/test".text = {
  "a" = "hello";
  "b" = "hi";
  "c" = "ciao";
}."${x}";
</syntaxHighlight>
 
So if <code>x</code> is set to the string <code>"a"</code> then the <code>just/for/test</code> file contents would be set to the string <code>"hello"</code>. The code above is the same in behavior to the following, more common, if-else-style construct:


consider using <code>import</code> here as using <code>qualified import ...</code> in Haskell or <code>import ...</code> in Python.
<syntaxHighlight lang=nix>
environment.etc."just/for/test".text =
  if x == "a" then
    "hello"
  else if x == "b" then
    "hi"
  else if x == "c" then
    "ciao"
  else
    abort "x is invalid";
</syntaxHighlight>


Another way of importing is <code>with import ...;</code>, which corresponds to Python <code>from ... import *</code>.
There is an example in <code>coq</code> package code [https://github.com/NixOS/nixpkgs/blob/5185539c51ba658e70b29e01c0c320a85f4e2098/pkgs/build-support/coq/extra-lib.nix#L98 here] where someone used this behavior to build a reusable function <code>switch</code>.


But because of not very great IDE support in Nix, <code>with import ...;</code> is discouraged. Rather use <code>inherit</code>, especially if you are targeting source code for Nix newcomers:
== <code>builtins.toString</code> handling of <code>true</code> and <code>false</code> is inconsistent ==


<nowiki>
<syntaxHighlight lang=nix>
let
nix-repl> builtins.toString true
  lib = import <nixpkgs/lib>;
"1"
  inherit (lib.strings)
    removePrefix removeSuffix
  ;
  inherit (lib.lists)
    isList init drop
  ;
in
  removePrefix ...</nowiki>


<code>inherit</code> has higher priority than <code>with</code>, and conflicts with <code>let</code>
nix-repl> builtins.toString false
<nowiki>
""
nix-repl> let pkgs = { x = 1; }; x = 2; x = 3; inherit (pkgs) x; in x
</syntaxHighlight>
error: attribute ‘x’ at (string):1:31 already defined at (string):1:24</nowiki>
This makes it a sane citizen of Nix lanugage... except it has a twin, called <code>{ inherit ...; }</code>. They DON'T do the same - <code>let inherit ...</code> adds let-bindings, and <code>{ inherit ...; }</code> adds attributes to a record.


== Q: What is the shortest <code>id</code> function definition? ==
== Nix Language FAQ ==
=== Q: What is the shortest <code>id</code> function definition? ===


A: <code>x: x</code>
A: <code>x: x</code>


Q: Why not <code>x:x</code>?
=== Q: Why not <code>x:x</code>? ===


A:
A:
<nowiki>
  <syntaxHighlight lang=nix>
nix-repl> builtins.typeOf (x: x)
nix-repl> builtins.typeOf (x: x)
"lambda"
"lambda"
nix-repl> builtins.typeOf (x:x)
nix-repl> builtins.typeOf (x:x)
"string"</nowiki>
"string" </syntaxHighlight>
! [https://github.com/NixOS/nix/issues/836 Can you figure out how can this happens before reading explanation?]
! [https://github.com/NixOS/nix/issues/836 Can you figure out how can this happens before reading explanation?]
{{note|[https://github.com/NixOS/nix/commit/f90f660b243866b8860eeb24cc4a345d32cc7ce7 Nix 1.12 should fix this]}}
=== Q: Can Nix code be interpolated? ===
No, only attribute names can.
<syntaxHighlight lang=nix>
nix-repl> let ${"x"} = 2; in x
2
nix-repl> with { ${"x"} = 2; }; x
2
nix-repl> let x = 1; y = ${x}; in y
error: syntax error, unexpected DOLLAR_CURLY, at (string):1:16</syntaxHighlight>
=== Q: Can it be <code>eval</code>-ed from string? ===
A: Yes, but it is not recommended as "eval" is generally regarded as an easy to abuse language feature. It is possible but only via the store (not as bad as "import from derivation", but still not suitable for hot code paths):
<syntaxHighlight lang=nix>
nix-repl> let code = "(x: x) ''id function was called''"; in import (builtins.toFile "eval" code)
"id function was called"</syntaxHighlight>
= Resources =
* [https://md.darmstadt.ccc.de/xtNP7JuIQ5iNW1FjuhUccw# A separately maintained list of Nix language quirks]
[[Category:Nix Language]]