Nix Language Quirks: Difference between revisions
imported>Danbst added x:x example |
imported>Danbst added interpolation/eval examples |
||
Line 104: | Line 104: | ||
"string"</nowiki> | "string"</nowiki> | ||
! [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:41, 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 ...
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.
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?
Yes, but not everywhere
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: Sure, but only via "import from derivation":
nix-repl> let code = "(x: x) ''id function was called''"; in import (builtins.toFile "eval" code) "id function was called"