How do you explore a nix expression?

Say, I got my configuration.nix, which is the entry point to the NixOS world, I’ve done a bunch of OS configs and the concept is wonderful and it is working out smoothly. However, to get which options I can set for a certain package, I’d now google up the module declaration on GitHub and read the docs there or guess the types. That’s very indirect and sometimes I get the wrong declaration, however that’s a go.

But now I’m trying to declare a new package for my needs, and place it alongside the configuration.nix and have it tangled in when building my configuration (rather than fork the full package repo and place it there), and things are going very slowly because for each symbol referenced in a normal configuration.nix I do not know a good way to (a) learn its exact type (in terms of typed languages, here that would be the common set of properties probably) and (b) which source file it were declared in, and what exactly the declaration does. Here I mean learning exactly, not guessing by randomly grepping around. I’m much used to good IDEs here, which let you quickly learn stuff incrementally by looking into how others’ things are done and crafting alike. And you get small but useful results on each step, which has a good feeling. Here I feel blindfolded somewhat.

So what are the good ways to explore what I got in expressions?

4 Likes

For the system configuration specifically, you can look up the possible options/descriptions/types/default values with man configuration.nix

So you want an intellisense for NixOS configuration? I want that as well!

I hope smbdy writes it, so it will eventually be available to all the munde. Meanwhile, we use nix repl and autocomplete:

$ nix repl '<nixpkgs>' '<nixpkgs/nixos>'

You can now poke around various options:

# in which files is this particular option set
nix-repl> :p options.environment.shellAliases.files
[ "/home/danbst/power-profile/profiles/base.nix" "/nix/var/nix/profiles/per-user/root/channels/nixpkgs/nixos/modules/config/shells-environment.nix" ]                   

# what values are defined in respective files
nix-repl> :p options.environment.shellAliases.definitions
[ { nix-env = /home/danbst/power-profile/experimental/declarative-env.sh; t = "/home/danbst/dev/todo.txt-cli/todo.sh"; } { l = { _type = "override"; content = "ls -alh"; priority = 1000; }; ll = { _type = "override"; content = "ls -l"; priority = 1000; }; ls = { _type = "override"; content = "ls --color=tty"; priority = 1000; }; } ]   

# in which files is this option created (declared)
nix-repl> :p options.environment.shellAliases.declarations
[ "/nix/var/nix/profiles/per-user/root/channels/nixpkgs/nixos/modules/config/shells-environment.nix" ]                                                                  

# what is the final value of this option (including all the defaults and overrides)
nix-repl> config.environment.shellAliases
{ l = "ls -alh"; ll = "ls -l"; ls = "ls --color=tty"; nix-env = /home/danbst/power-profile/experimental/declarative-env.sh; t = "/home/danbst/dev/todo.txt-cli/todo.sh"; }

# read option description in-place
nix-repl> options.environment.shellAliases.description
"An attribute set that maps aliases (the top level attribute names in\nthis option) to command strings or directly to build outputs. The\naliases are added to all users' shells.\nAliases mapped to <code>null</code> are ignored.\n"

You can also inspect various other internals:

nix-repl> lib.types.<TAB-TAB>
lib.types.addCheck          lib.types.defaultTypeMerge  lib.types.isOptionType      lib.types.nonEmptyListOf    lib.types.setType           lib.types.uniq
lib.types.attrs             lib.types.either            lib.types.isType            lib.types.nullOr            lib.types.shellPackage      lib.types.unspecified
lib.types.attrsOf           lib.types.enum              lib.types.lines             lib.types.optionSet         lib.types.str
lib.types.bool              lib.types.envVar            lib.types.list              lib.types.package           lib.types.strMatching
lib.types.coercedTo         lib.types.float             lib.types.listOf            lib.types.path              lib.types.string
lib.types.commas            lib.types.int               lib.types.loaOf             lib.types.port              lib.types.submodule
lib.types.defaultFunctor    lib.types.ints              lib.types.mkOptionType      lib.types.separatedString   

nix-repl> lib.strings.<TAB-TAB>
lib.strings.addContextFrom        lib.strings.fileContents          lib.strings.makeBinPath           lib.strings.stringLength
lib.strings.concatImapStrings     lib.strings.fixedWidthNumber      lib.strings.makeLibraryPath       lib.strings.stringToCharacters
lib.strings.concatImapStringsSep  lib.strings.fixedWidthString      lib.strings.makeSearchPath        lib.strings.substring
lib.strings.concatMapStrings      lib.strings.getVersion            lib.strings.makeSearchPathOutput  lib.strings.tail
lib.strings.concatMapStringsSep   lib.strings.hasInfix              lib.strings.nameFromURL           lib.strings.toInt
lib.strings.concatStrings         lib.strings.hasPrefix             lib.strings.optionalString        lib.strings.toLower
lib.strings.concatStringsSep      lib.strings.hasSuffix             lib.strings.readPathsFromFile     lib.strings.toUpper
lib.strings.enableFeature         lib.strings.head                  lib.strings.removePrefix          lib.strings.upperChars
lib.strings.enableFeatureAs       lib.strings.intersperse           lib.strings.removeSuffix          lib.strings.versionAtLeast
lib.strings.escape                lib.strings.isCoercibleToString   lib.strings.replaceChars          lib.strings.versionOlder
lib.strings.escapeNixString       lib.strings.isStorePath           lib.strings.replaceStrings        lib.strings.withFeature
lib.strings.escapeShellArg        lib.strings.isString              lib.strings.splitString           lib.strings.withFeatureAs
lib.strings.escapeShellArgs       lib.strings.lowerChars            lib.strings.stringAsChars

nix-repl> lib.modules.mk<TAB-TAB>
lib.modules.mkAfter                         lib.modules.mkChangedOptionModule           lib.modules.mkOrder
lib.modules.mkAliasAndWrapDefinitions       lib.modules.mkDefault                       lib.modules.mkOverride
lib.modules.mkAliasAndWrapDefsWithPriority  lib.modules.mkFixStrictness                 lib.modules.mkRemovedOptionModule
lib.modules.mkAliasDefinitions              lib.modules.mkForce                         lib.modules.mkRenamedOptionModule
lib.modules.mkAliasIfDef                    lib.modules.mkIf                            lib.modules.mkStrict
lib.modules.mkAliasOptionModule             lib.modules.mkMerge                         lib.modules.mkVMOverride
lib.modules.mkAssert                        lib.modules.mkMergedOptionModule
lib.modules.mkBefore                        lib.modules.mkOptionDefault
14 Likes

Thanks, this is a way (though slow), personally I’ve been mostly looking up things in GitHub - NixOS/nixpkgs: Nix Packages collection & NixOS in options declarations for the package of interest.

1 Like

nix-repl! How could I forget. That’s a really good point, thanks. Though not ideal for the very beginning because it does not explain your existing configuration files as they are. But as this is a dynamic language, that would have been a bit too much to expect probably.

1 Like

@danbst not on computer so can’t check but is this documented? Ultra useful and I will document in the manual if not.

1 Like

I think this was never documented, tacit knowledge :slight_smile:

https://github.com/NixOS/nixpkgs/pull/71120

If I may say so, the manual format is unnecessarily complex.

Wellll not ultra useful but of a limited use actually. I’ve been able to put together a derivation I needed and mount it locally into configuration.nix without forking nixpkgs, but mostly by copypasting, guesswork, reading error messages of nixos-rebuild process (many of them surprisingly helpful) and googling answers to some other questions. Repl does show something, but first you have to guess how to fire it up for your current configuration, and then it didn’t help me with the error “is not of type package”, like, at all – systemPackages takes what it thinks to be a derivation expression and mine were as well, and I got nowhere close to seeing repl speak any types like package, I still do not quite know what this means, isn’t a nix expression type to my limited knowledge of nix. Fortunately, the error were classic and I could google it. So there’s still a gap between making up your first dozen working configurations and understanding how they actually run behind the curtains, and nix repl isn’t helping much.

1 Like

btw, any ideas how to make this process easier? What are the core missing debugging tools? Or maybe some concepts should be simplified?

It’s not exactly what you are asking for but there is a project for diffing Nix derivations, which can be used to see the differences between two realized configurations.

There are some projects that strive to implement the Language Server Protocol for Nix and thus can enable in the far future, intellisense completion builtin to your $EDITOR, here are implementations I’m aware of:

And there’s also tree-sitter-nix written by @cstrahan.

1 Like