Nix Language Quirks: Difference between revisions

From NixOS Wiki
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>:


  <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.


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.
<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 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:


  <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 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:


<nowiki>
  <syntaxHighlight lang=nix>
let
let
   lib = import <nixpkgs/lib>;
   lib = import <nixpkgs/lib>;
Line 83: Line 83:
   ;
   ;
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.


Line 98: Line 98:


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?]
== Q: Can Nix code be interpolated? ==
Yes, but not everywhere
<nowiki>
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</nowiki>
Q: Can it be <code>eval</code>-ed from string?
A: Sure, but only via "import from derivation":
<nowiki>
nix-repl> let code = "(x: x) ''id function was called''"; in import (builtins.toFile "eval" code)
"id function was called"</nowiki>

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.

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

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?