2021-04-19 04:39:35 +03:00
|
|
|
# Nickel
|
|
|
|
|
2021-04-19 13:09:51 +03:00
|
|
|
[![Continuous integration](https://github.com/tweag/nickel/workflows/Continuous%20integration/badge.svg)](https://github.com/tweag/nickel/actions?query=branch%3Amaster)
|
2022-06-09 14:50:03 +03:00
|
|
|
[![Website](https://img.shields.io/website-up-down-green-red/http/cv.lbesson.qc.to.svg)](https://nickel-lang.org)
|
2021-04-19 13:09:51 +03:00
|
|
|
|
2021-04-19 04:39:35 +03:00
|
|
|
Nickel is the cheap configuration language.
|
|
|
|
|
|
|
|
Its purpose is to automate the generation of static configuration files - think
|
|
|
|
JSON, YAML, XML, or your favorite data representation language - that are then
|
|
|
|
fed to another system. It is designed to have a simple, well-understood core: it
|
|
|
|
is in essence JSON with functions.
|
|
|
|
|
|
|
|
Nickel's salient traits are:
|
|
|
|
|
|
|
|
- **Lightweight**: Nickel is easy to embed. An interpreter should be simple to
|
|
|
|
implement. The reference interpreter can be called from many programming
|
|
|
|
languages.
|
|
|
|
- **Composable code**: the basic building blocks for computing are functions.
|
|
|
|
They are first-class citizens, which can be passed around, called and
|
|
|
|
composed.
|
2021-04-19 10:29:49 +03:00
|
|
|
- **Composable data**: the basic building blocks for data are records
|
2021-04-19 04:39:35 +03:00
|
|
|
(called *objects* in JSON). In Nickel, records can be merged at will,
|
|
|
|
including associated metadata (documentation, default values, type
|
|
|
|
contracts, etc).
|
2021-04-19 10:38:01 +03:00
|
|
|
- **Typed, but only when it helps**: static types improve code quality, serve as
|
2021-04-19 04:39:35 +03:00
|
|
|
documentation and eliminate bugs early. But application-specific
|
|
|
|
self-contained code will always evaluate to the same value, so type errors
|
|
|
|
will show up at runtime anyway. Some JSON is hard to type. There, types are
|
|
|
|
only a burden. Whereas reusable code - that is, *functions* - is evaluated
|
|
|
|
on potentially infinitely many different inputs, and is impossible to test
|
|
|
|
exhaustively. There, types are precious. Nickel has types, but you get to
|
2021-10-29 03:06:18 +03:00
|
|
|
choose when you want it or not, and it handles safely the interaction between
|
2021-04-19 04:39:35 +03:00
|
|
|
the typed and the untyped world.
|
|
|
|
- **Design by contract**: complementary to the type system, contracts are
|
|
|
|
a principled approach to checking assertions. The interpreter automatically
|
|
|
|
inserts assertions at the boundary between typed and untyped code. Nickel
|
|
|
|
lets users add arbitrary assertions of their own and easily understand why
|
2021-04-19 13:09:51 +03:00
|
|
|
when assertions fail.
|
2020-07-30 14:36:49 +03:00
|
|
|
|
|
|
|
The motto guiding Nickel's design is:
|
|
|
|
> Great defaults, design for extensibility
|
|
|
|
|
2021-04-19 04:39:35 +03:00
|
|
|
There should be a standard, clear path for common things. There should be no
|
|
|
|
arbitrary restrictions that limit what you can do you the one day you need to go
|
|
|
|
beyond.
|
2020-07-30 14:36:49 +03:00
|
|
|
|
|
|
|
## Use cases
|
|
|
|
|
2021-10-29 03:06:18 +03:00
|
|
|
Nickel is a good fit in any situation where you need to generate a complex
|
2021-04-19 04:39:35 +03:00
|
|
|
configuration, be it for a single app, a machine, whole infrastructure, or a
|
2020-07-30 14:36:49 +03:00
|
|
|
build system.
|
|
|
|
|
|
|
|
The motivating use cases are in particular:
|
2022-08-04 18:31:21 +03:00
|
|
|
|
2020-07-30 14:36:49 +03:00
|
|
|
- The [Nix package manager](https://nixos.org/): Nix is a declarative package
|
2021-04-19 04:39:35 +03:00
|
|
|
manager using its own language for specifying packages. Nickel is an
|
|
|
|
evolution of the Nix language, while trying to overcome some of its
|
|
|
|
limitations.
|
|
|
|
- Infrastructure as code: infrastructure is becoming increasingly complex,
|
|
|
|
requiring a rigorous approach to deployment, modification and configuration.
|
|
|
|
This is where a declarative approach also shines, as adopted by
|
|
|
|
[Terraform](https://www.terraform.io/),
|
2020-07-30 14:36:49 +03:00
|
|
|
[NixOps](https://github.com/NixOS/nixops) or
|
|
|
|
[Kubernetes](https://kubernetes.io/), all requiring potentially complex
|
|
|
|
generation of configuration.
|
2021-04-19 04:39:35 +03:00
|
|
|
- Build systems: build systems (like [Bazel](https://bazel.build/)) need
|
|
|
|
a specification of the dependency graph.
|
2020-07-30 14:36:49 +03:00
|
|
|
|
2021-04-19 04:39:35 +03:00
|
|
|
Most aforementioned projects have their own bespoke configuration language. See
|
2022-08-04 18:31:21 +03:00
|
|
|
[Related projects and inspirations](#related-projects-and-inspirations). In
|
2021-04-19 04:39:35 +03:00
|
|
|
general, application-specific languages might suffer from feature creep, lack of
|
|
|
|
abstractions or just feel ad hoc. Nickel buys you more for less.
|
2020-07-30 14:36:49 +03:00
|
|
|
|
|
|
|
## Getting started
|
|
|
|
|
2022-06-09 14:50:03 +03:00
|
|
|
Please follow the getting started guide for Nickel users on the [nickel-lang
|
|
|
|
website](https://nickel-lang.org/getting-started). The instructions below are
|
|
|
|
either reproduced for this document to be relatively self-sufficient, or because
|
|
|
|
they are aimed toward hacking on the Nickel interpreter itself (e.g. building
|
|
|
|
the `nickel-lang` crate documentation).
|
|
|
|
|
2020-07-30 14:36:49 +03:00
|
|
|
### Run
|
|
|
|
|
2021-05-31 15:28:33 +03:00
|
|
|
1. Start Nickel
|
2022-08-04 18:31:21 +03:00
|
|
|
|
|
|
|
- with [flake-enabled](https://nixos.wiki/wiki/Flakes) Nix directly
|
2021-11-17 14:25:22 +03:00
|
|
|
with `nix run nickel` (which pulls it from the global flakes
|
|
|
|
registry), or with `nix run github:tweag/nickel` (which pulls it
|
|
|
|
from the repo). You can use [our binary cache](https://nickel.cachix.org) to
|
|
|
|
prevent rebuilding a lot of packages. You pass in arguments with
|
|
|
|
an extra `--` as in `nix run nickel -- repl`,
|
2022-08-04 18:31:21 +03:00
|
|
|
- with `./nickel`, after [building](#build) this repo, depending on the
|
2021-05-31 15:34:40 +03:00
|
|
|
location of the executable and passing in arguments directly,
|
2022-08-04 18:31:21 +03:00
|
|
|
- or with `cargo run` after [building](#build), passing in arguments with
|
2021-11-17 21:08:14 +03:00
|
|
|
an extra `--` as in `cargo run -- -f program.ncl`.
|
2021-04-30 22:43:24 +03:00
|
|
|
|
2020-07-30 14:36:49 +03:00
|
|
|
2. Run your first program:
|
2022-08-04 18:31:21 +03:00
|
|
|
|
|
|
|
```console
|
|
|
|
$ ./nickel <<< 'let x = 2 in x + x'
|
|
|
|
4
|
|
|
|
```
|
|
|
|
|
|
|
|
Or load it from a file:
|
|
|
|
|
|
|
|
```console
|
|
|
|
$ echo 'let s = "world" in "Hello, " ++ s' > program.ncl
|
|
|
|
$ ./nickel -f program.ncl
|
|
|
|
"Hello, world"
|
|
|
|
```
|
|
|
|
|
2021-08-10 11:34:49 +03:00
|
|
|
3. Start a REPL:
|
2021-03-29 11:36:54 +03:00
|
|
|
|
2022-08-04 18:31:21 +03:00
|
|
|
```console
|
|
|
|
$ ./nickel repl
|
|
|
|
nickel> let x = 2 in x + x
|
|
|
|
4
|
|
|
|
|
|
|
|
nickel>
|
|
|
|
```
|
2021-08-10 11:34:49 +03:00
|
|
|
|
2022-08-04 18:31:21 +03:00
|
|
|
Use `:help` for a list of available commands.
|
2021-03-29 11:36:54 +03:00
|
|
|
4. Export your configuration to JSON, YAML or TOML:
|
2022-08-04 18:31:21 +03:00
|
|
|
|
2021-08-23 14:56:25 +03:00
|
|
|
```console
|
2021-04-19 04:39:35 +03:00
|
|
|
$ ./nickel export --format json <<< '{foo = "Hello, world!"}'
|
2021-03-29 11:36:54 +03:00
|
|
|
{
|
|
|
|
"foo": "Hello, world!"
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2021-03-31 12:48:57 +03:00
|
|
|
Use `nickel help` for a list of subcommands, and `nickel help <subcommand>`
|
2021-03-29 11:36:54 +03:00
|
|
|
for help about a specific subcommand.
|
2020-07-30 14:36:49 +03:00
|
|
|
|
2022-01-18 20:22:57 +03:00
|
|
|
#### Editor Setup
|
|
|
|
|
2022-08-04 18:31:21 +03:00
|
|
|
Nickel has syntax highlighting plugins for Vim/Neovim, and VSCode. In-editor
|
|
|
|
diagnostics, type hints, and auto-completion are provided by the Nickel Language
|
|
|
|
Server. Please follow
|
|
|
|
[this guide](https://github.com/tweag/nickel/tree/master/lsp) to setup syntax
|
|
|
|
highlighting and NLS.
|
2022-01-18 20:22:57 +03:00
|
|
|
|
2021-04-30 22:47:41 +03:00
|
|
|
### Build
|
|
|
|
|
|
|
|
[rust-guide]: https://doc.rust-lang.org/cargo/getting-started/installation.html
|
|
|
|
|
|
|
|
1. Download build dependencies:
|
2022-08-04 18:31:21 +03:00
|
|
|
|
2021-04-30 22:47:41 +03:00
|
|
|
- **With Nix**: If you have [Nix](https://nixos.org/nix) installed:
|
2022-08-04 18:31:21 +03:00
|
|
|
|
2021-08-23 14:56:25 +03:00
|
|
|
```console
|
2022-08-04 18:31:21 +03:00
|
|
|
nix-shell shell.nix
|
2021-04-30 22:47:41 +03:00
|
|
|
```
|
2022-08-04 18:31:21 +03:00
|
|
|
|
2021-11-17 14:25:22 +03:00
|
|
|
to be dropped in a shell, ready to build. You can use [our binary
|
2022-08-04 18:31:21 +03:00
|
|
|
cache](https://nickel.cachix.org) to prevent rebuilding a lot of packages.
|
2021-04-30 22:47:41 +03:00
|
|
|
- **Without Nix**: otherwise, follow [this guide][rust-guide] to install Rust
|
|
|
|
and Cargo first.
|
2022-08-04 18:31:21 +03:00
|
|
|
|
2022-01-01 10:07:16 +03:00
|
|
|
1. Build Nickel:
|
2022-08-04 18:31:21 +03:00
|
|
|
|
2021-08-23 14:56:25 +03:00
|
|
|
```console
|
2022-08-04 18:31:21 +03:00
|
|
|
cargo build
|
2021-04-30 22:47:41 +03:00
|
|
|
```
|
2022-08-04 18:31:21 +03:00
|
|
|
|
2021-04-30 22:47:41 +03:00
|
|
|
And voilà! Generated files are placed in `target/debug`.
|
|
|
|
1. *(optional)* make a symbolic link to the executable:
|
2022-08-04 18:31:21 +03:00
|
|
|
|
2021-08-23 14:56:25 +03:00
|
|
|
```console
|
2022-08-04 18:31:21 +03:00
|
|
|
ln -S nickel target/debug/nickel
|
2021-04-30 22:47:41 +03:00
|
|
|
```
|
|
|
|
|
2020-07-30 14:36:49 +03:00
|
|
|
### Tests
|
2021-04-19 04:39:35 +03:00
|
|
|
|
2021-08-23 14:56:25 +03:00
|
|
|
```console
|
2022-08-04 18:31:21 +03:00
|
|
|
cargo test
|
2020-07-30 14:36:49 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
### Documentation
|
2021-04-19 04:39:35 +03:00
|
|
|
|
2022-06-09 14:50:03 +03:00
|
|
|
The user manual is available [on the nickel-lang.org
|
|
|
|
website](https://nickel-lang.org/user-manual/introduction), and in this
|
|
|
|
repository as a collection of Markdown files in `doc/manual`.
|
|
|
|
|
|
|
|
To get the documentation of the `nickel-lang` codebase itself:
|
|
|
|
|
2020-07-30 14:36:49 +03:00
|
|
|
1. Build the doc:
|
2022-08-04 18:31:21 +03:00
|
|
|
|
|
|
|
```console
|
|
|
|
cargo doc --no-deps
|
|
|
|
```
|
|
|
|
|
2020-07-30 14:36:49 +03:00
|
|
|
2. Open the file `target/doc/nickel/index.html` in your browser.
|
|
|
|
|
|
|
|
### Examples
|
2021-04-19 04:39:35 +03:00
|
|
|
|
2022-03-08 11:27:29 +03:00
|
|
|
You can find examples in the [`./examples`](./examples) directory.
|
|
|
|
|
|
|
|
## Current state and roadmap
|
|
|
|
|
|
|
|
Nickel has been released in version `0.1`. This version should be functional, it
|
|
|
|
is intended to gather feedback and real-life testing. Nickel `0.1` isn't intended
|
|
|
|
to be used in production. The next steps we plan to work on are:
|
|
|
|
|
|
|
|
- Nix integration: being able to seamlessly use Nickel to write shells, packages
|
|
|
|
and NixOS modules.
|
|
|
|
- Custom merge functions and priorities (second part of the
|
2022-03-14 15:35:31 +03:00
|
|
|
[overriding proposal](https://github.com/tweag/nickel/blob/9fd6e436c0db8f101d4eb26cf97c4993357a7c38/rfcs/001-overriding.md))
|
2022-03-08 11:27:29 +03:00
|
|
|
- Performance improvements
|
2020-07-30 14:36:49 +03:00
|
|
|
|
|
|
|
## Related projects and inspirations
|
2021-04-19 04:39:35 +03:00
|
|
|
|
2022-05-21 01:44:59 +03:00
|
|
|
- [CUE](https://cuelang.org/) is a configuration language with a focus on data
|
2021-04-19 04:39:35 +03:00
|
|
|
validation. It introduces a new constraint system backed by a solid theory
|
2020-07-30 14:36:49 +03:00
|
|
|
which ensures strong guarantees about your code. It allows for very elegant
|
|
|
|
schema specifications. In return, the cost to pay is to abandon functions
|
|
|
|
and
|
|
|
|
[Turing-completeness](https://en.wikipedia.org/wiki/Turing_completeness).
|
|
|
|
Nickel's merge system is inspired by the one of CUE, even if since Nickel
|
|
|
|
does have general functions and is Turing-complete, they are necessarily
|
|
|
|
different.
|
|
|
|
- [Nix](https://nixos.org/): The Nix language, or *Nix expressions*, is one of
|
2021-08-10 11:34:49 +03:00
|
|
|
the main inspirations for Nickel. It is a very simple yet powerful lazy
|
2020-07-30 14:36:49 +03:00
|
|
|
functional language. We strive to retain this simplicity, while adding
|
|
|
|
typing capabilities, modularity, and detaching the language from the Nix
|
|
|
|
package manager.
|
|
|
|
- [Dhall](https://dhall-lang.org/) is a statically typed configuration language.
|
|
|
|
It is also inspired by Nix, to which it adds a powerful static type system.
|
2020-08-10 10:34:27 +03:00
|
|
|
However, this forces the programmer to annotate all of their code with types.
|
2020-07-30 14:36:49 +03:00
|
|
|
- [Jsonnet](https://jsonnet.org/) is another language which could be dubbed as
|
|
|
|
"JSON with functions" (and others things as well). It is a lazy functional
|
|
|
|
language with object oriented features, among which inheritance is similar
|
|
|
|
to Nickel's merge system. One big difference with Nickel is the absence of
|
|
|
|
typing.
|
|
|
|
- [Pulumi](https://www.pulumi.com/) is not a language in itself, but a cloud
|
|
|
|
tool (like Terraform) where you can use your preferred language for
|
|
|
|
describing your infrastructure. This is a different approach to the problem,
|
|
|
|
with different trade-offs.
|
|
|
|
- [Starlark](https://docs.bazel.build/versions/master/skylark/language.html) is
|
2021-04-19 04:39:35 +03:00
|
|
|
the language of [Bazel](https://bazel.build/), which is a dialect of
|
|
|
|
[Python](https://www.python.org/). It does not have types and recursion is
|
|
|
|
forbidden, making it not Turing-complete.
|
2020-07-30 14:36:49 +03:00
|
|
|
|
2020-08-12 18:56:40 +03:00
|
|
|
See [RATIONALE.md](./RATIONALE.md) for the design rationale and a more detailed
|
|
|
|
comparison with a selection of these languages.
|