Nix Language Quirks: Difference between revisions
imported>Danbst added interpolation/eval examples |
imported>Makefu <nowiki> -> <syntaxHighlight lang=nix> |
||
Line 5: | Line 5: | ||
<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 then <code>let</code>. This can lead to confusions, especially if you like to write <code>with pkgs;</code>: | ||
< | <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</ | 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? | ||
< | <syntaxHighlight lang=nix> | ||
nix-repl> let x = 2; in with pkgs; x | nix-repl> let x = 2; in with pkgs; x | ||
2</ | 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 | ||
< | <syntaxHighlight lang=nix> | ||
nix-repl> let { x = 1; y = x + 1; body = y; } | nix-repl> let { x = 1; y = x + 1; body = y; } | ||
2</ | 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. | ||
Line 36: | Line 36: | ||
== 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. | ||
<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</ | 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 | ||
< | <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</ | 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>: | ||
<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</ | 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 61: | ||
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: | ||
< | <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 "....</ | 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 73: | ||
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: | ||
<syntaxHighlight lang=nix> | |||
let | let | ||
lib = import <nixpkgs/lib>; | lib = import <nixpkgs/lib>; | ||
Line 83: | Line 83: | ||
; | ; | ||
in | in | ||
removePrefix ...< | 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> | ||
<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</ | 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. | ||
Line 98: | Line 98: | ||
A: | A: | ||
<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"</ | "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?] | ||
Revision as of 11:51, 26 October 2017
with
and let
with
gets less priority then 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.
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
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 ... <syntaxHighlight>
<code>inherit</code> has higher priority than <code>with</code>, and conflicts with <code>let</code>
<syntaxHighlight lang=nix>
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.
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?