-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
lesson/improve getting started section (#14)
* Started reworking the lesson that introduces what modules are. Working state. * Added an attribution section to the README. * Move module evaluation discussion to a new lesson. * Moved a link that got accidently transfered. * Added a note about modules being attrsets. * Added the function arguments section content. * Updated the beginning and added a definition section.
- Loading branch information
Showing
6 changed files
with
193 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,157 @@ | ||
# A Module | ||
# What is a module | ||
|
||
In this lesson, we will cover what a module is composed of. | ||
In this lesson, you will learn what a module is and how to define one. | ||
|
||
In the [eval][eval] file, we have declared an attrset called `mymodule` which is composed of 3 | ||
fields: `imports`, `options` and `config`. This is a module with default values for those fields. | ||
You can omit any of those fields and they will use those same default values. | ||
## Definition | ||
|
||
[//]: # (./eval.nix) | ||
A *module* is a function that returns an attrset. | ||
It declares options with types. | ||
It defines option values. | ||
When evaluated, it produces a configuration based on the declarations and definitions. | ||
|
||
If you execute the run file (`./run`), you will see printed the empty JSON object `{}`. | ||
If that does not make a lot of sense, do not worry. | ||
Keep reading. | ||
The rest of this lesson will provide clarity. | ||
|
||
[eval]: ./eval.nix | ||
## Structure | ||
|
||
## Module Fields | ||
### An empty module | ||
|
||
The `options` field lets you define variables that can be used in the `config` section. | ||
Modules have the following basic structure. | ||
|
||
The `config` field assigns values to options defined in other modules. We call this using an option. | ||
```nix title="useless.nix" | ||
{...}: { | ||
} | ||
``` | ||
|
||
The `imports` field lets you import other modules from a module. | ||
This, as the filename suggests, is completely useless. | ||
It takes no arguments and returns an empty attrset. | ||
However, it does evaluate. | ||
So when we look at the following examples, keep in mind that everything we add is optional. | ||
|
||
We will see what goes in those fields in later lessons. | ||
### A basic module | ||
|
||
## Evaluating Modules | ||
Let us look at a more practical setup. | ||
|
||
In the [eval][eval] file, we use a function called `evalModules`. This function takes an attrset as | ||
argument which can contain the field named `modules`. In there, we can put as many module as we | ||
want. | ||
```nix title="default.nix" | ||
{lib, ...}: { | ||
imports = [ | ||
./relative/path/to/another/module.nix | ||
/absolute/path/to/another/module.nix | ||
]; | ||
options = { | ||
# declarations of options | ||
}; | ||
}; | ||
config = { | ||
# configuration of options | ||
}; | ||
} | ||
``` | ||
|
||
`evalModules` will then merge all `imports`, `options` and `config` fields of all given modules | ||
following some rules we will see in the next lessons. It then produces an attrset whose only | ||
interesting field - the result of merging all modules - is found in the `config` field. This is the | ||
one we print when executing the `./run` file. | ||
!!! note | ||
`options` and `config` and have formal names — | ||
that is ***option declarations*** and ***option definitions*** respectively. | ||
The rest of these lessons will use them interchangeably. | ||
|
||
## Nixpkgs | ||
#### function argument | ||
|
||
The vast majority of nixpkgs is made out of modules, all merged together in the top-level | ||
`evalModules`. | ||
Now the module is a function which takes *at least* one argument, `lib`, | ||
and may accept other arguments (expressed by the ellipsis `...`). | ||
This will make Nixpkgs library functions available within the function body. | ||
|
||
!!! note | ||
The ellipsis `...` is necessary because arbitrary arguments can be passed to modules. | ||
Every module you create that is a function, should have this. | ||
|
||
The `lib` argument is passed automatically by the module system. | ||
It is absolutely vital for modules that have option declarations, as you will need `lib` for defining options and their types. | ||
It is one of several arguments that are automatically provided by the module system. | ||
The full list of arguments is discussed later. | ||
|
||
#### imports | ||
|
||
This imports list enumerates the paths to other NixOS modules. | ||
This is a useful mechanism for breaking up modules into small components and importing what you need. | ||
|
||
#### options | ||
|
||
To set any values, the module system first has to know which ones are allowed. | ||
|
||
This is done by declaring options that specify which values can be set and used elsewhere. | ||
Options are declared by adding an attribute under the top-level `options` attribute. | ||
The most general way to declare an option is using `lib.mkOption`. | ||
|
||
``` nix title="options.nix" | ||
{lib, ...}: { | ||
options = { | ||
name = lib.mkOption { | ||
type = lib.types.str; | ||
}; | ||
}; | ||
} | ||
``` | ||
|
||
While many attributes for customizing options are available, | ||
the most important one is `type`, | ||
which specifies which values are valid for an option. | ||
There are several types available under [`lib.types`][option-types-basic] in the Nixpkgs library. | ||
|
||
Here we have declared an option `name` with the `str` type. | ||
This specifies that the only valid value is a string and can only be a single definition. | ||
|
||
#### config | ||
|
||
Option definitions are generally straight-forward bindings of values to option names. | ||
|
||
``` nix title="config.nix" | ||
{ | ||
config.name = "Boaty McBoatface"; | ||
} | ||
``` | ||
|
||
!!! note | ||
Modules do not have to be functions. | ||
They can be attrsets as well. | ||
The above `config.nix` file is a valid module. | ||
Generally, you can write modules that are only option definitions as attrsets. | ||
|
||
Note that our option declarations and option definitions do not need to exist in the same file. | ||
When we evaluate our modules, we can simply include both files. | ||
As long as every definition has a declaration, we can successfully evaluate our modules. | ||
If there is an option definition that has not been declared, the module system will throw an error. | ||
|
||
## Function Arguments | ||
|
||
When you define a module as a function like we did previously, there are certain arguments that are automatically provided. | ||
You ***do not*** have to explicitely put them in the function signature as we have the ellipsis `...` to manage any arguments we do not use. | ||
|
||
All modules are passed the following arguments: | ||
|
||
- `lib`: The nixpkgs library. | ||
- `config`: The results of all options after merging the values from all modules together. | ||
- `options`: The options declared in all modules. | ||
- `specialArgs`: An attribute set of extra arguments to be passed to the module functions. | ||
- All attributes of `specialArgs`. | ||
|
||
!!! note | ||
The fact that all the attributes of `specialArgs` are automatically provided means you don't need to add `specialArgs` to the module function signature if we want access to `specialArgs.thing`. | ||
We can just add `thing` to the function signature and use it directly. | ||
|
||
When designing a module for NixOS, there are some additional arguments that are automatically provided: | ||
|
||
- `pkgs`: The nixpkgs package set according to the `nixpkgs.pkgs` option. | ||
- `modulesPath`: The path to the NixOS modules directory in the nixpkgs repository. | ||
|
||
`modulesPath` is very handy as it allows you to import extra modules from the nixpkgs package tree without having to somehow make the module aware of the location of the `nixpkgs` or NixOS directories. | ||
It allows you to do things like this: | ||
|
||
``` nix | ||
{ modulesPath, ... }: { | ||
imports = [ | ||
(modulesPath + "/profiles/minimal.nix") | ||
]; | ||
} | ||
``` | ||
|
||
[option-types-basic]: https://nixos.org/manual/nixos/stable/#sec-option-types-basic |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# Module Evaluation | ||
|
||
In the [eval][eval] file, we have declared an attrset called `mymodule` which is composed of 3 | ||
fields: `imports`, `options` and `config`. This is a module with default values for those fields. | ||
You can omit any of those fields and they will use those same default values. | ||
|
||
[//]: # (./eval.nix) | ||
|
||
If you execute the run file (`./run`), you will see printed the empty JSON object `{}`. | ||
|
||
[eval]: ./eval.nix | ||
|
||
## Module Fields | ||
|
||
The `options` field lets you define variables that can be used in the `config` section. | ||
|
||
The `config` field assigns values to options defined in other modules. We call this using an option. | ||
|
||
The `imports` field lets you import other modules from a module. | ||
|
||
We will see what goes in those fields in later lessons. | ||
|
||
## Evaluating Modules | ||
|
||
In the [eval][eval] file, we use a function called `evalModules`. This function takes an attrset as | ||
argument which can contain the field named `modules`. In there, we can put as many module as we | ||
want. | ||
|
||
`evalModules` will then merge all `imports`, `options` and `config` fields of all given modules | ||
following some rules we will see in the next lessons. It then produces an attrset whose only | ||
interesting field - the result of merging all modules - is found in the `config` field. This is the | ||
one we print when executing the `./run` file. | ||
|
||
## Nixpkgs | ||
|
||
The vast majority of nixpkgs is made out of modules, all merged together in the top-level | ||
`evalModules`. | ||
|
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5c013bb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉 Published on https://nixos-modules.nix.xn--q9jyb4c as production
🚀 Deployed on https://65ca50eda6262f2d5c1962ca--nixos-modules-lessons.netlify.app