Nix Language Quirks

From NixOS Wiki
Jump to: navigation, search

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

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

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

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

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; }

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 { }

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; }

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:

  pkgs = import <nixpkgs> {};
  lib = import <nixpkgs/lib>;
  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:

  lib = import <nixpkgs/lib>;
  inherit (lib.strings)
    removePrefix removeSuffix
  inherit (lib.lists)
    isList init drop
  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 ""


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 whitespace is removed also in single-line Indented Strings

''  s  '' == "s  "

Usually, Indented Strings have multiple lines

'' == "s\n"

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

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

Nix Language FAQ

Q: What is the shortest id function definition?

A: x: x

Q: Why not x:x?


nix-repl> builtins.typeOf (x: x)
nix-repl> builtins.typeOf (x:x)

! 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

nix-repl> with { ${"x"} = 2; }; x

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"