Jump to content

Nix Language Quirks

From NixOS Wiki
Revision as of 20:53, 13 September 2022 by imported>Fricklerhandwerk (@ syntax does not bind default values)
⤟︎
This article or section is a candidate for merging with Nix Expression Language. Further information may be found in the relevant discussion page.

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"


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?

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"