Getting started

Nickel is still young, and the installation process is not yet optimal. Sorry about that! We are focused on improving the experience, so stay tuned.

We give three alternative ways of obtaining a running Nickel executable:
  1. Using Nix
  2. Using Cargo
  3. Using Docker
If you have neither of these tools, Nix is the preferred way. If you don't have Nix but you are a user of Cargo or Docker, you may prefer the corresponding installation method.

Get a Nickel binary using Nix

Run

With a recent version of Nix (> 2.4.0), you can build and run Nickel in one shot. If you haven't installed Nix yet, please follow this installation guide. Once Nix is installed, use nix run to start Nickel and append -- args to pass arguments to the Nickel executable (here we launch an REPL session)

nix run --experimental-features "flakes nix-command" github:tweag/nickel/stable -- repl
nickel>

Install

If you are planning to try Nickel a bit more extensively, you can install the Nickel binary using nix profile:

nix profile install github:tweag/nickel/stable
nickel repl
nickel>

Get a Nickel binary using Cargo

If you are a Rust developer, the Cargo build tool is an alternative way to install a Nickel binary:

cargo install nickel-lang-cli
nickel repl
nickel>

Install with Homebrew

If you're running macOS you can use Homebrew to install the Nickel binary.

brew install nickel
nickel repl
nickel>

Get a Docker image

A last alternative is to use Docker image:

docker run --rm -it ghcr.io/tweag/nickel:1.4.0 repl
nickel>

Write your first configuration

Nickel has advanced features to help you handle and organize complex configurations (gradual typing, contracts, a merge system, and so on). But you'll only have to deal with any of this once you need to. Writing a basic configuration is as simple as writing JSON or YAML. Let's write a manifest of a fictional app:

{
  name = "example",
  description = m%"
    This is an awesome software I'm developing.
    Please use it!
  "%,
  version = "0.1.1",
  main = "index.js",
  keywords = ["example", "config"],
  scripts = {
    test = m%"
      test.sh --option --install example --version "0.1.1"
    "%,
    do_stuff = "do_stuff.sh subcommand",
  },
  contributors = [{
      name = "John Doe",
      email = "johndoe@example.com"
    }, {
      name = "Ivy Lane",
      url = "https://example.com/ivylane"
    }],
  dependencies = {
    dep1 = "^1.0.0",
    dep3 = "6.7"
  }
}

This program is composed of a record. A record is the same thing as an object in JSON, that is a list of key-value pairs delimited by { and }. In general, Nickel values map directly to values in JSON, excluding functions. Thus, the basic datatypes of Nickel are the same as in JSON:
  • Records (objects), delimited by { and }.
  • Strings, delimited by ". The sequences m%" and "% delimit multiline strings.
  • Numbers.
  • Arrays, delimited by [ and ] and separated by ,.

Multiline strings are an alternative way of writing string literals. Line 11 is an example of such a string. Without diving into the details, multiline strings are useful for:
  • Writing strings spanning several lines, as their name suggests. Multiline strings can be indented at the same level as the surrounding code while still producing the expected result: the common indentation prefix is stripped.
  • Writing strings with special characters without having to escape them.
In our example, using a multiline string saves us from escaping the recurring double quotes ".

Export & Import

The ultimate goal of a Nickel program is to produce a static configuration. To do so, save the example above to example.ncl and run nickel export:

nickel export example.ncl --format yaml
---
contributors:
  - email: johndoe@example.com
    name: John Doe
  - name: Ivy Lane
    url: https://example.com/ivylane
dependencies:
  dep1: ^1.0.0
  dep3: "6.7"
description: "This is awesome software I'm developing.\nPlease use it!"
keywords:
  - example
  - config
main: index.js
name: example
scripts:
  do_stuff: do_stuff.sh subcommand
  test: "test.sh --option --install example --version \"0.1.1\""
version: 0.1.1

Nickel currently supports exporting to and importing from YAML, TOML and JSON. Importing an existing configuration into a Nickel one is as easy as writing import "something.yaml". For example, if our contributor data is already stored in a YAML file contributors.yaml and we want to gradually migrate the manifest to Nickel, we could import contributors.yaml as a first step:

{
  # [...]
  scripts = {
    test = m%"
      test.sh --option --install example --version "0.1.1"
    "%,
    do_stuff = "do_stuff.sh subcommand",
  },
  contributors = import "contributors.yaml",
  # [...]
}

Reuse

Nickel is a programming language. This allows you to not only describe, but generate data. There's repetition in our previous example:

name = "example",
version = "0.1.1",
scripts = {
  test = m%"
    test.sh --option --install example --version "0.1.1"
  "%,

The version 0.1.1 appears both in version and scripts.test. The name example appears both in name and scripts.test as well. Pure aesthetics aside, a more serious issue is inconsistency. If you bump the version number in version, you may forget to do so in the scripts.test as well, ending up with incoherent version numbers in the same configuration. To remedy the problem, let's have a single source of truth by reusing the value of name and version in scripts.test, using the string interpolation syntax %{expr}:

name = "example",
version = "0.1.1",
scripts = {
  test = m%"
    test.sh --option --install %{name} --version "%{version}"
  "%,

Now, if we change version to 0.1.2 and export the result, the test script invocation is updated as well:

# [...]
scripts:
  do_stuff: do_stuff.sh subcommand
  test: "test.sh --option --install example --version \"0.1.2\""
version: 0.1.2

Going further

This short introduction should get you started. Nickel is a full-fledged programming language, featuring higher-order functions, gradual typing, contracts, and more! To explore further, read the user manual. In particular, the last section is a slighty more advanced tutorial. You will also find examples in the repository. For an overview of Nickel and the motivations behind it, see the README and the design rationale document.