Nix Language Quirks: Difference between revisions

From NixOS Wiki
imported>Danbst
added x:x example
H7x4 (talk | contribs)
Add entry about using null as attrset key
 
(18 intermediate revisions by 8 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.
Nah, <code>with</code> and <code>let</code> have different priority when resolving names.
Line 27: Line 27:


This is an old Nix syntax, that probably isn't used much
This is an 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 @ 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 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.
Line 61: Line 75:
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:
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:


  <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 using <code>import</code> here as using <code>qualified import ...</code> in Haskell or <code>import ...</code> in Python.  
consider using <code>import</code> here as using <code>qualified import ...</code> in Haskell or <code>import ...</code> in Python.  
Line 73: Line 87:
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:
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:


<nowiki>
  <syntaxHighlight lang=nix>
let
let
   lib = import <nixpkgs/lib>;
   lib = import <nixpkgs/lib>;
Line 83: Line 97:
   ;
   ;
in
in
   removePrefix ...</nowiki>
   removePrefix ... </syntaxHighlight>


<code>inherit</code> has higher priority than <code>with</code>, and conflicts with <code>let</code>  
<code>inherit</code> has higher priority than <code>with</code>, and conflicts with <code>let</code>  
<nowiki>
 
  <syntaxHighlight lang=nix>
nix-repl> let pkgs = { x = 1; }; x = 2; x = 3; inherit (pkgs) x; in x
nix-repl> let pkgs = { x = 1; }; x = 2; x = 3; inherit (pkgs) x; in x
error: attribute ‘x’ at (string):1:31 already defined at (string):1:24</nowiki>
error: attribute ‘x’ at (string):1:31 already defined at (string):1:24 </syntaxHighlight>
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.
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? ==
== builtins.replaceStrings key match on "" ==
 
Syntax:
<syntaxHighlight lang=nix>
builtins.replaceStrings [match] [replace] string
</syntaxHighlight>
 
Function allows match for "" in string. <code>[match]</code> gets checked sequentially, and when "" is checked - it always matches. And so - when "" is checked in always inserts the replacement, then next char in sting gets passed through, and the next char after that from the string 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 String trims leading whitespace ==
 
Not really surprising, but ...
 
Leading spaces are removed also in single-line Indented Strings
 
<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>
 
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 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>
 
== Using null as key in attribute set deletes the attribute ==
 
<syntaxHighlight lang=nix>
nix-repl> { ${null} = "value"; key = "value2"; }
{ key = "value2"; }
</syntaxHighlight>
 
== 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>
[[Category:Nix Language]]

Latest revision as of 11:28, 5 August 2024

with and let

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

nix-repl> pkgs = { x = 1; }

nix-repl> with pkgs; x
1

nix-repl> with pkgs; let x = 2; in x
2

So we see, that let binding overrides with binding. But what about this?

nix-repl> let x = 2; in with pkgs; x
2

Nah, with and let have different priority when resolving names.

Good discussion on this topic

Old let syntax

This is an old Nix syntax, that probably isn't used much

nix-repl> let { x = 1; y = x + 1; body = y; }
2

It is equivalent to modern syntax expression let x = 1; y = x + 1; in y. Note, that it doesn't require rec keyword.

Note, that it isn't equivalent to with rec { x = 1; y = x + 1; body = y; }; body because of mentioned with and let quirk, but is same as rec { x = 1; y = x + 1; body = y; }.body

Default values are not bound in @ 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 "a" to the argument's attribute a with an empty attribute set as argument will produce an empty attribute set args instead of { a = "a"; }:

(args@{a ? "a"}: args) {}

{ }

Related: GitHub issue filed 2017

Something that looks like both record attribute and let-binding

Destructuring function argument - is a great feature of Nix.

nix-repl> f = { x ? 1, y ? 2 }: x + y

nix-repl> f { }
3

The fact that we can add @args argument assignment is also cool

nix-repl> f = { x ? 1, y ? 2, ... }@args: with args; x + y + z

nix-repl> f { z = 3; }
6

But don't be fooled, args doesn't necessarily contain x and y:

nix-repl> f = { x ? 1, y ? 2, ... }@args: args.x + args.y + args.z

nix-repl> f { z = 3;}
error: attribute x missing, at (string):1:30

These x and y are in fact let-bindings, but overridable ones.

Imports and namespaces

There is a keyword import, but it's equivalent in other languages is eval. It can be used for namespacing too:

let
  pkgs = import <nixpkgs> {};
  lib = import <nixpkgs/lib>;
in
  pkgs.runCommand (lib.strings.removePrefix "....

consider using import here as using qualified import ... in Haskell or import ... in Python.

Another way of importing is with import ...;, which corresponds to Python from ... import *.

But because of not very great IDE support in Nix, with import ...; is discouraged. Rather use inherit, especially if you are targeting source code for Nix newcomers:

let
  lib = import <nixpkgs/lib>;
  inherit (lib.strings)
    removePrefix removeSuffix
  ;
  inherit (lib.lists)
    isList init drop
  ;
in
  removePrefix ...

inherit has higher priority than with, and conflicts with let

nix-repl> let pkgs = { x = 1; }; x = 2; x = 3; inherit (pkgs) x; in x
error: attribute x at (string):1:31 already defined at (string):1:24

This makes it a sane citizen of Nix lanugage... except it has a twin, called { inherit ...; }. They DON'T do the same - let inherit ... adds let-bindings, and { inherit ...; } adds attributes to a record.

builtins.replaceStrings key match on ""

Syntax:

builtins.replaceStrings [match] [replace] string

Function allows match for "" in string. [match] gets checked sequentially, and when "" is checked - it always matches. And so - when "" is checked in always inserts the replacement, then next char in sting gets passed through, and the next char after that from the string gets processed.

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"

Indented String trims leading whitespace

Not really surprising, but ...

Leading spaces are removed also in single-line Indented Strings

''  s  '' == "s  "

Usually, Indented Strings have multiple lines

''
  s
'' == "s\n"

Though note that tab characters are not stripped:

''
	s
'' ==  "	s\n"

See NixOS/nix#7834 and NixOS/nix#9971 for more information.

Integer precision

Integer precision is limited to 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

nix-repl> 9223372036854775807 + 1
-9223372036854775808

Invalid integer literals throw

nix-repl> 9223372036854775808  
error: invalid integer '9223372036854775808'

No negative number literals

Negative numbers are parsed as "zero minus positive"

nix-instantiate --parse --expr '(-1)'
(__sub 0 1)

So this throws, because the positive number is out of range

nix-repl> -9223372036854775808
error: invalid integer '9223372036854775808'

but this works

nix-repl> -9223372036854775807 - 1
-9223372036854775808

Using null as key in attribute set deletes the attribute

nix-repl> { ${null} = "value"; key = "value2"; }
{ key = "value2"; }

Nix Language FAQ

Q: What is the shortest id function definition?

A: x: x

Q: Why not x:x?

A:

nix-repl> builtins.typeOf (x: x)
"lambda"
nix-repl> builtins.typeOf (x:x)
"string"

! Can you figure out how can this happens before reading explanation?

Q: Can Nix code be interpolated?

No, only attribute names can.

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

Q: Can it be eval-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):

nix-repl> let code = "(x: x) ''id function was called''"; in import (builtins.toFile "eval" code)
"id function was called"