Top Related Projects
Maintainable configuration files
Starlark in Go: the Starlark configuration language, implemented in Go
HCL is the HashiCorp configuration language.
Tom's Obvious, Minimal Language
Quick Overview
Nickel is a configuration language designed for building and deploying software systems. It aims to be expressive, typed, and easily extensible, providing a robust foundation for managing complex configurations across various environments and applications.
Pros
- Strong typing system that helps catch errors early in the development process
- Extensible design allowing for custom functions and modules
- Supports both declarative and imperative programming styles
- Integrates well with existing build and deployment pipelines
Cons
- Relatively new language, which may lead to a smaller community and fewer resources
- Learning curve for developers unfamiliar with functional programming concepts
- Limited ecosystem compared to more established configuration languages
- May require additional tooling or integration work for some existing systems
Code Examples
- Basic configuration example:
let config = {
app = {
name = "MyApp",
version = "1.0.0",
port = 8080,
},
database = {
url = "postgres://localhost:5432/myapp",
max_connections = 10,
},
}
in config
- Using functions and conditionals:
let env = std.env.get "ENVIRONMENT" | default "development"
let db_config = if env == "production" then {
url = "postgres://prod-db:5432/myapp",
max_connections = 50,
} else {
url = "postgres://localhost:5432/myapp",
max_connections = 10,
}
in { database = db_config }
- Custom function example:
let generate_api_key = fun length =>
std.string.join "" (std.array.generate length (fun _ => std.random.choice "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"))
let config = {
api_key = generate_api_key 32,
# ... other configuration options
}
in config
Getting Started
To get started with Nickel:
- Install Nickel following the instructions at https://github.com/tweag/nickel#installation
- Create a new file with a
.ncl
extension (e.g.,config.ncl
) - Write your configuration using Nickel syntax
- Run your configuration file using the Nickel CLI:
nickel eval config.ncl
For more advanced usage and integration with your projects, refer to the official documentation and examples in the Nickel repository.
Competitor Comparisons
Maintainable configuration files
Pros of Dhall
- More mature and established project with a larger community
- Stronger focus on type safety and correctness
- Better documentation and learning resources
Cons of Dhall
- Less flexible and expressive compared to Nickel
- Steeper learning curve for users new to functional programming
- Limited support for dynamic evaluation and runtime features
Code Comparison
Dhall example:
let Person = { name : Text, age : Natural }
let alice : Person = { name = "Alice", age = 30 }
in alice
Nickel example:
let Person = {
name : String,
age : Number,
}
let alice = {
name = "Alice",
age = 30,
} | Person
alice
Both Dhall and Nickel are configuration languages designed to improve upon traditional formats like JSON or YAML. Dhall emphasizes strong typing and safety, while Nickel offers more flexibility and dynamic features. Dhall has a larger ecosystem and community support, but Nickel's design allows for more expressive configurations and easier integration with existing systems. The choice between the two depends on specific project requirements and the balance between type safety and flexibility needed in the configuration language.
Starlark in Go: the Starlark configuration language, implemented in Go
Pros of Starlark-go
- More mature and widely adopted, with a larger community and ecosystem
- Designed for configuration and scripting, making it suitable for various use cases
- Backed by Google, ensuring long-term support and development
Cons of Starlark-go
- Less focus on contract-based programming and type safety compared to Nickel
- Limited built-in support for handling complex data structures and schemas
- Lacks some of the advanced features found in Nickel, such as default values and merging
Code Comparison
Nickel:
let config = {
port = 8080,
debug = true,
users = [
{ name = "Alice", role = "admin" },
{ name = "Bob", role = "user" },
],
}
Starlark:
config = {
"port": 8080,
"debug": True,
"users": [
{"name": "Alice", "role": "admin"},
{"name": "Bob", "role": "user"},
],
}
Both languages offer similar syntax for defining configuration data, but Nickel provides more advanced features for working with complex configurations and ensuring type safety.
HCL is the HashiCorp configuration language.
Pros of HCL
- Widely adopted and well-established in the industry, especially for infrastructure-as-code
- Simpler syntax, easier to learn for users familiar with JSON or YAML
- Strong integration with HashiCorp ecosystem (Terraform, Vault, etc.)
Cons of HCL
- Less expressive and flexible compared to Nickel's functional programming features
- Limited type system, which can lead to less robust configurations
- Lacks advanced features like gradual typing and contract programming
Code Comparison
HCL:
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
}
Nickel:
let aws = import "aws.ncl" in
{
resource.aws_instance.example = {
ami = "ami-0c55b159cbfafe1f0",
instance_type = "t2.micro",
}
}
Summary
HCL is a more established and widely used configuration language, particularly in the infrastructure-as-code domain. It offers a simpler syntax that's easier to learn for those familiar with JSON or YAML. However, Nickel provides more advanced features like functional programming constructs, gradual typing, and contract programming, making it more flexible and expressive for complex configurations. The choice between the two depends on the specific use case and the level of complexity required in the configuration language.
Tom's Obvious, Minimal Language
Pros of TOML
- Simpler syntax and easier to read for humans
- Widely adopted and supported in many programming languages
- Excellent for configuration files and small data structures
Cons of TOML
- Limited expressiveness compared to Nickel's advanced features
- Lacks support for complex data structures and dynamic configurations
- No built-in validation or type checking capabilities
Code Comparison
TOML:
[server]
host = "localhost"
port = 8080
[database]
url = "postgresql://user:password@localhost/mydb"
Nickel:
{
server = {
host = "localhost",
port = 8080,
},
database = {
url = "postgresql://user:password@localhost/mydb",
},
config | {
server.port | Number,
database.url | String,
}
}
The Nickel example demonstrates its ability to include type annotations and validation directly in the configuration file, which is not possible in TOML. Nickel also offers more flexibility in terms of dynamic configurations and complex data structures, while TOML focuses on simplicity and readability for basic configuration needs.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Nickel
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.
- Composable data: the basic building blocks for data are records (called objects in JSON). In Nickel, records can be merged at will, including associated metadata (documentation, default values, type contracts, etc).
- Typed, but only when it helps: static types improve code quality, serve as 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 choose when you want it or not, and it handles safely the interaction between 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 when assertions fail.
The motto guiding Nickel's design is:
Great defaults, design for extensibility
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.
Use cases
Nickel is a good fit in any situation where you need to generate a complex configuration, be it for a single app, a machine, whole infrastructure, or a build system.
The motivating use cases are in particular:
- The Nix package manager: Nix is a declarative package 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, NixOps or Kubernetes, all requiring potentially complex generation of configuration.
- Build systems: build systems (like Bazel) need a specification of the dependency graph.
Most aforementioned projects have their own bespoke configuration language. See Comparison. In general, application-specific languages might suffer from feature creep, lack of abstractions or just feel ad hoc. Nickel buys you more for less.
The Nickel ecosystem
Related projects that are part of the Nickel ecosystem:
- Terraform-Nickel: write Terraform configuration with Nickel
- Organist: batteries included environments with Nickel inside
- json-schema-to-nickel: generate Nickel contracts from JSON schema specifications.
- rules_nickel: generate configuration files using Nickel during a Bazel build
- The nickel-lang organization hosts various smaller projects, including a tree-sitter grammar definition for Nickel and editor plugins.
Getting started
Please follow the getting started guide for Nickel users on the nickel-lang
website. The instructions below are
either reproduced for this document to be self-contained or because
they are aimed toward hacking on the Nickel interpreter itself (e.g. building
the nickel-lang-core
crate documentation).
Run
-
Get a Nickel binary:
- With flake-enabled Nix, run
Nickel directly with
nix run github:tweag/nickel
. You can use our binary cache to prevent rebuilding a lot of packages. Pass arguments to Nickel with an extra--
as innix run github:tweag/nickel -- repl
, - Again with flake-enabled Nix, you can install Nickel in your profile with
nix profile install github:tweag/nickel
. Thenickel
command is then in your$PATH
and is available anywhere. - If you're running macOS you can use Homebrew to install the Nickel binary
with
brew install nickel
. - Without Nix, you can use
cargo run --bin nickel
after building, passing arguments with an extra--
as incargo run --bin nickel -- eval program.ncl
.
- With flake-enabled Nix, run
Nickel directly with
-
Run your first program:
$ nickel eval <<< '["hello", "world"] |> std.string.join ", "' "hello, world"
Or load it from a file:
$ echo 'let s = "world" in "hello, %{s}"' > program.ncl $ nickel eval program.ncl "hello, world"
-
Start a REPL:
$ nickel repl nickel> {"hello" = true, "world" = true, "universe" = false} |> std.record.to_array |> std.array.filter (fun {field, value} => value) |> std.array.map (fun {field, value} => field) |> std.string.join ", " "hello, world"
Use
:help
for a list of available commands. -
Export your configuration to JSON, YAML or TOML:
$ nickel export --format json <<< '{content = "hello, world"}'
{
"content": "hello, world"
}
Use nickel help
for a list of subcommands, and nickel help <subcommand>
for help about a specific subcommand.
To get in touch, you can join our server.
Editor Setup
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 the LSP guide to set up syntax highlighting and NLS.
Formatting
To format one or several Nickel source files, use nickel format
:
nickel format network.ncl container.ncl api.ncl
Nickel uses Topiary to format Nickel code under the hood.
Please follow the Formatting Capabilities section of the LSP documentation to know how to hook up the Nickel LSP and Topiary in order to enable formatting inside your code editor.
Build
-
Download build dependencies:
-
With Nix: If you have Nix installed:
nix-shell # if you don't use Nix flakes nix develop # if you use Nix flakes
You will be dropped in a shell, ready to build. You can use our binary cache to prevent rebuilding a lot of packages.
-
Without Nix: otherwise, follow this guide to install Rust and Cargo first.
-
-
Build Nickel:
cargo build -p nickel-lang-cli --release
And voilà ! Generated files are placed in
target/release
.You can directly build and run the Nickel binary and pass argument after
--
by usingcargo run
:cargo run --bin nickel -- eval foo.ncl
Test
Run tests with
cargo test
Documentation
The user manual is available on the nickel-lang.org
website, and in this
repository as a collection of Markdown files in doc/manual
.
To get the documentation of the nickel-lang
codebase itself:
-
Build the doc:
cargo doc --no-deps
-
Open the file
target/doc/nickel/index.html
in your browser.
Examples
You can find examples in the ./examples
directory.
Current state and roadmap
Since version 1.0 released in May 2023, the core design of the language is stable and Nickel is useful for real-world applications. The next steps we plan to work on are:
The next steps we plan to work on are:
- Nix integration: being able to seamlessly use Nickel to write packages and shells (Organist)
- Custom merge functions (second part of the overriding proposal)
- Incremental evaluation: design an incremental evaluation model and a caching mechanism in order to perform fast re-evaluation upon small changes to a configuration.
- Performance improvements
Comparison
- CUE is a configuration language with a focus on data validation. It introduces a new constraint system backed by a solid theory 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. 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: The Nix language, or Nix expressions, is one of the main inspirations for Nickel. It is a very simple yet powerful lazy functional language. We strive to retain this simplicity, while adding typing capabilities, modularity, and detaching the language from the Nix package manager.
- Dhall is a statically typed configuration language. It is also inspired by Nix, to which it adds a powerful static type system. However, this forces the programmer to annotate all of their code with types.
- Jsonnet 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.
- KCL is a gradually typed configuration language whose validation is based on object-oriented schemas that can be extended through inheritance. Unlike the languages above, its evaluation is strict.
- Pulumi 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 is the language of Bazel, which is a dialect of Python. It does not have types and recursion is forbidden, making it not Turing-complete.
See RATIONALE.md for the design rationale and a more detailed comparison with these languages.
Comparison with other configuration languages
Language | Typing | Recursion | Evaluation | Side-effects |
---|---|---|---|---|
Nickel | Gradual (dynamic + static) | Yes | Lazy | Yes (constrained, planned) |
Starlark | Dynamic | No | Strict | No |
Nix | Dynamic | Yes | Lazy | Predefined and specialized to package management |
Dhall | Static (requires annotations) | Restricted | Lazy | No |
CUE | Static (everything is a type) | No | Lazy | No, but allowed in the separated scripting layer |
Jsonnet | Dynamic | Yes | Lazy | No |
KCL | Gradual (dynamic + static) | Yes | Strict | No |
JSON | None | No | Strict | No |
YAML | None | No | N/A | No |
TOML | None | No | N/A | No |
Top Related Projects
Maintainable configuration files
Starlark in Go: the Starlark configuration language, implemented in Go
HCL is the HashiCorp configuration language.
Tom's Obvious, Minimal Language
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot