1
1
mirror of https://github.com/tweag/nickel.git synced 2024-09-11 11:47:03 +03:00

update comment syntax in anual, rfcs and READMEs

This commit is contained in:
francois-caddet 2022-03-03 11:35:18 +01:00
parent 5fdc352391
commit feb41abbf2
12 changed files with 148 additions and 148 deletions

View File

@ -2,12 +2,12 @@ Why Nickel ?
============
There already exist quite a few languages with a similar purpose to Nickel:
[CUE](https://cuelang.org/), [Dhall](https://dhall-lang.org/),
[Jsonnet](https://jsonnet.org/),
[Starlark](https://docs.bazel.build/versions/master/skylark/language.html), to
[CUE](https:#cuelang.org/), [Dhall](https:#dhall-lang.org/),
[Jsonnet](https:#jsonnet.org/),
[Starlark](https:#docs.bazel.build/versions/master/skylark/language.html), to
mention the closest contenders. So why Nickel ?
Nickel originated as an effort to detach the [Nix](https://nixos.org/)
Nickel originated as an effort to detach the [Nix](https:#nixos.org/)
expression language from the Nix package manager, while adding typing
capabilities and improve modularity. We found that in practice, Nix is a simple
yet expressive language which is particularly well fitted to build programmable
@ -144,8 +144,8 @@ never requires recursion, this is not the case with library code. Allowing
recursion makes it possible for programmers to implement new generic
functionalities \[2\].
\[1\]: [Why Dhall is not Turing complete](http://neilmitchell.blogspot.com/2020/11/turing-incomplete-languages.html)\
\[2\]: [Turing incomplete languages](http://www.haskellforall.com/2020/01/why-dhall-advertises-absence-of-turing.html)
\[1\]: [Why Dhall is not Turing complete](http:#neilmitchell.blogspot.com/2020/11/turing-incomplete-languages.html)\
\[2\]: [Turing incomplete languages](http:#www.haskellforall.com/2020/01/why-dhall-advertises-absence-of-turing.html)
### Side-Effects
As for Turing-completeness, most of these languages also forbid side-effects.
@ -160,7 +160,7 @@ variables.
However, sometimes the situation does not fit in a rigid framework: as for
Turing-completeness, there may be cases which mandates side-effects. An example
is when writing [Terraform](https://www.terraform.io/) configurations, some
is when writing [Terraform](https:#www.terraform.io/) configurations, some
external values (an IP) used somewhere in the configuration may only be known
once another part of the configuration has been evaluated and executed
(deploying machines, in this context). Reading this IP is a side-effect, even if
@ -176,7 +176,7 @@ specific use-cases.
Let's compare Nickel with the languages cited at the beginning: Starlark, Dhall, CUE and Jsonnet.
### Starlark: the standard package
Starlark is a language originally designed for the [Bazel](https://bazel.build/)
Starlark is a language originally designed for the [Bazel](https:#bazel.build/)
build system, but it can also be used independently as a configuration language.
It is a dialect of Python and includes the following classical features:
@ -198,7 +198,7 @@ code and prevents the expression of data schemas inside the language.
### Dhall: powerful type system
Dhall is heavily inspired by Nix, to which it adds a [powerful type
system](https://github.com/dhall-lang/dhall-lang/blob/master/standard/README.md#summary).
system](https:#github.com/dhall-lang/dhall-lang/blob/master/standard/README.md#summary).
Because of its complexity, the type system only supports a limited type
inference. This can lead to code that is sometimes heavy on type annotations,
as in the following example:

View File

@ -1,6 +1,6 @@
# Nickel
[![Continuous integration](https://github.com/tweag/nickel/workflows/Continuous%20integration/badge.svg)](https://github.com/tweag/nickel/actions?query=branch%3Amaster)
[![Continuous integration](https:#github.com/tweag/nickel/workflows/Continuous%20integration/badge.svg)](https:#github.com/tweag/nickel/actions?query=branch%3Amaster)
Nickel is the cheap configuration language.
@ -50,18 +50,18 @@ 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](https://nixos.org/): Nix is a declarative package
- The [Nix package manager](https:#nixos.org/): 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](https://www.terraform.io/),
[NixOps](https://github.com/NixOS/nixops) or
[Kubernetes](https://kubernetes.io/), all requiring potentially complex
[Terraform](https:#www.terraform.io/),
[NixOps](https:#github.com/NixOS/nixops) or
[Kubernetes](https:#kubernetes.io/), all requiring potentially complex
generation of configuration.
- Build systems: build systems (like [Bazel](https://bazel.build/)) need
- Build systems: build systems (like [Bazel](https:#bazel.build/)) need
a specification of the dependency graph.
Most aforementioned projects have their own bespoke configuration language. See
@ -74,10 +74,10 @@ abstractions or just feel ad hoc. Nickel buys you more for less.
### Run
1. Start Nickel
* with [flake-enabled](https://nixos.wiki/wiki/Flakes) Nix directly
* with [flake-enabled](https:#nixos.wiki/wiki/Flakes) Nix directly
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
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`,
* with `./nickel`, after [building](#Build) this repo, depending on the
@ -121,19 +121,19 @@ for help about a specific subcommand.
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.
Please follow [this guide](https:#github.com/tweag/nickel/tree/master/lsp) to setup syntax highlighting and NLS.
### Build
[rust-guide]: https://doc.rust-lang.org/cargo/getting-started/installation.html
[rust-guide]: https:#doc.rust-lang.org/cargo/getting-started/installation.html
1. Download build dependencies:
- **With Nix**: If you have [Nix](https://nixos.org/nix) installed:
- **With Nix**: If you have [Nix](https:#nixos.org/nix) installed:
```console
$ nix-shell shell.nix
```
to be dropped in a shell, ready to build. You can use [our binary
cache](https://nickel.cachix.org) to prevent rebuilding a lot of
cache](https:#nickel.cachix.org) to prevent rebuilding a lot of
packages.
- **Without Nix**: otherwise, follow [this guide][rust-guide] to install Rust
and Cargo first.
@ -175,43 +175,43 @@ and other important practical aspects are still being debated. We aim to
transition from an experimental stage to a minimum viable product stage. The
next points to deal with are:
- [Stdlib stabilization](https://github.com/tweag/nickel/issues/321)
- [Overriding](https://github.com/tweag/nickel/pull/330)
- [Stdlib stabilization](https:#github.com/tweag/nickel/issues/321)
- [Overriding](https:#github.com/tweag/nickel/pull/330)
- Memory management (use reference counting) & basic performance improvements
- [List comprehensions](https://github.com/tweag/nickel/issues/80)
- [Destructuring](https://github.com/tweag/nickel/issues/81)
- [List comprehensions](https:#github.com/tweag/nickel/issues/80)
- [Destructuring](https:#github.com/tweag/nickel/issues/81)
## Related projects and inspirations
- [Cue](https://cuelang.org/) is a configuration language with a focus on data
- [Cue](https:#cuelang.org/) 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](https://en.wikipedia.org/wiki/Turing_completeness).
[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
- [Nix](https:#nixos.org/): 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](https://dhall-lang.org/) is a statically typed configuration language.
- [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.
However, this forces the programmer to annotate all of their code with types.
- [Jsonnet](https://jsonnet.org/) is another language which could be dubbed as
- [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
- [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
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
- [Starlark](https:#docs.bazel.build/versions/master/skylark/language.html) is
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.
See [RATIONALE.md](./RATIONALE.md) for the design rationale and a more detailed

View File

@ -15,10 +15,10 @@ let x = (1 + 1 | Num) in x
Contract can also be attached to identifiers in a definition:
```nickel
// let-binding: equivalent to the previous example
# let-binding: equivalent to the previous example
let x | Num = 1 + 1 in x
// on a record field
# on a record field
{x | Num = 1 + 1}
```
@ -165,7 +165,7 @@ let Between = fun min max =>
contract.from_predicate (fun value =>
value >= min &&
value <= max) in
// alternative without from_predicate
# alternative without from_predicate
let BetweenAlt = fun min max label value =>
if builtin.is_num value &&
value >= min &&
@ -197,9 +197,9 @@ let Nullable = fun contract label value =>
value
else
contract.apply contract label value in
// succeeds
# succeeds
null | Nullable Num
// succeeds too
# succeeds too
1 | Nullable Num
```
@ -580,7 +580,7 @@ let NumBoolDict = fun label value =>
|> record.fields
|> array.foldl (fun acc field_name =>
if string.is_match "^\\d+$" field_name then
acc // unused and always null through iteration
acc # unused and always null through iteration
else
contract.blame_with "field name `#{field_name}` is not a number" label
) null in
@ -628,7 +628,7 @@ Let us see if we indeed preserved laziness:
```
nickel>let config | NumBoolDict = {
"1" = 1 + "a", // Same as our previous "fail"
"1" = 1 + "a", # Same as our previous "fail"
"0" | doc "Some information" = true,
}
nickel>:query config."0"

View File

@ -37,7 +37,7 @@ Alternatively, you can repeat your types both at the function level and at the
record level. It makes code more navigable and `query`-friendly, but at the
expense of repetition and duplicated contract checks. It is also currently
required for polymorphic functions because of [the following
bug](https://github.com/tweag/nickel/issues/360). A better solution will
bug](https:#github.com/tweag/nickel/issues/360). A better solution will
probably be implemented in the future: type holes (TODO: MAY ACTUALLY BE AVAILABLE FOR RELEASE)
(NOT YET POSSIBLE)

View File

@ -40,7 +40,7 @@ machinery. On the other hand, checking this property at runtime on the final
result is trivial.
Nevertheless, if you have ever faced puzzling [dynamic type
errors](https://www.haskellforall.com/2021/01/dynamic-type-errors-lack-relevance.html),
errors](https:#www.haskellforall.com/2021/01/dynamic-type-errors-lack-relevance.html),
you may feel the need for something better. Bare dynamic typing is prone to
irrelevant error messages, pointing to a location far from the problematic code
in the source. This is especially true when working with functions, which may be
@ -118,12 +118,12 @@ Here is the definition for `split`, but with a twist. We mistakenly forgot to
wrap `pair.key` as an array before concatenating at line 6:
```nickel
// lib.ncl
# lib.ncl
{
split = fun pairs =>
array.fold (fun pair acc =>
{
// problem: the right expression to use is [pair.key]
# problem: the right expression to use is [pair.key]
keys = acc.keys @ pair.key,
values = acc.values @ [pair.value],
})
@ -135,7 +135,7 @@ wrap `pair.key` as an array before concatenating at line 6:
And we call to split from our configuration:
```nickel
// config.ncl
# config.ncl
let {split} = import "lib.ncl" in
split [{key = "foo", value = 1}, {key = "bar", value = 2}]
```

View File

@ -79,7 +79,7 @@ let Schema = {
## Computation (compound expressions)
Some expressions are neither immediate data nor functions. Take for example the
function application `array.map (fun s => "http://#{s}/index") servers`.
function application `array.map (fun s => "http:##{s}/index") servers`.
Usually, you should do **nothing**.
- *Inside configuration: nothing*. The function or operator you are using should

View File

@ -99,15 +99,15 @@ block*.
Example:
```
// Let binding
# Let binding
let f : Num -> Bool = fun x => x % 2 == 0 in
// Record field
# Record field
let r = {
count : Num = 2354.45 * 4 + 100,
} in
// Inline
# Inline
1 + ((if f 10 then 1 else 0) : Num)
```
@ -300,7 +300,7 @@ write a type anntation. Here, `filter` is inferred to be of type `(Num -> Bool)
**Note**:
if you are a more type-inclined reader, you may wonder why the typechecker is
not capable of inferring a polymorphic type for `filter` by itself. Indeed,
[Hindley-Milner](https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system)
[Hindley-Milner](https:#en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system)
type-inference can precisely infer heading `foralls`, such that the previous
rejected example would be accepted. We chose to abandon this so-called automatic
generalization, because doing so just makes things simpler with respect to the
@ -389,7 +389,7 @@ summed, without modifying the rest:
```nickel
let sum : forall r. {a : Num, b : Num | r} -> {a : Num, b : Num, sum : Num | r}
= fun x => x $[ "sum" = x.a + x.b]
in sum {a = 1, b = 2, c = 3} // {a=1, b=2, sum=3, c=3}
in sum {a = 1, b = 2, c = 3} # {a=1, b=2, sum=3, c=3}
```
Note that row polymorphism also works with enums, with the same intuition of a

View File

@ -20,7 +20,7 @@ This example defines a couple contracts:
appears as such in the output. This contract is illustrative and by no mean
exhaustive.
- `Path`: define a valid `/`-separated path as a string. In particular, the
`//` sequence is forbidden.
`#` sequence is forbidden.
- `SharedObjectFile`: define a file whose extension is `.so`.
- `OptLevel`: optimization level, either `0`, `1` or `2`.
- `Contract`: the schema of the end configuration.

View File

@ -1,8 +1,8 @@
# Nickel Language Server
The Nickel Language Server (NLS) is a [language
server](https://en.wikipedia.org/wiki/Language_Server_Protocol) for the
[Nickel](https://www.nickel-lang.org/) programming language. NLS offers error
server](https:#en.wikipedia.org/wiki/Language_Server_Protocol) for the
[Nickel](https:#www.nickel-lang.org/) programming language. NLS offers error
messages, type hints, and auto-completion right in your favorite LSP-enabled
editor.
@ -18,7 +18,7 @@ use the Rust toolchain and don't want to install Nix.
### Using Nix (flakes)
The easiest way to install `nls` is using [Nix](https://nixos.org/).
The easiest way to install `nls` is using [Nix](https:#nixos.org/).
**Important**: the following of this section assumes that you have a flake-enabled
Nix (>= 2.4) and the experimental features `flakes` and `nix-command` enabled.
@ -51,19 +51,19 @@ Alternatively, you can insall `nickel` and `nls` globally on older Nix versions
without flakes via `nix-env`:
```
git clone https://github.com/tweag/nickel.git
git clone https:#github.com/tweag/nickel.git
cd nickel
nix-env -f . -i
```
### Using Cargo
If you already have a working [`cargo`](https://doc.rust-lang.org/cargo/) installation, you can make `nls` available
If you already have a working [`cargo`](https:#doc.rust-lang.org/cargo/) installation, you can make `nls` available
globally (it will be built and stored inside the repository) without
Nix:
```
git clone https://github.com/tweag/nickel.git
git clone https:#github.com/tweag/nickel.git
cd nickel/lsp/nls
cargo install --path .
```
@ -81,7 +81,7 @@ NLS is currently not available through the vscode marketplace, but this
repository includes an extension that can be built locally via Nix (cf []()
about the Nix setup).
- One-liner (using the [`jq`](https://stedolan.github.io/jq/) command):
- One-liner (using the [`jq`](https:#stedolan.github.io/jq/) command):
```
code --install-extension $(nix build ./\#vscodeExtension --no-link --json | jq ".[0].outputs.vsix")
```
@ -103,7 +103,7 @@ The VS Code extension offers three configuration options:
### (Neo)Vim
Before proceeding install the [Nickel syntax highlighting plugin](https://github.com/nickel-lang/vim-nickel) using your Vim plugin manager.
Before proceeding install the [Nickel syntax highlighting plugin](https:#github.com/nickel-lang/vim-nickel) using your Vim plugin manager.
Without this plugin your LSP client may not start NLS on nickel source files.
With Vim-Plug:
@ -114,7 +114,7 @@ Plug 'nickel-lang/vim-nickel'
#### Neovim builtin LSP
`nls` is supported in
[nvim-lspconfig](https://github.com/neovim/nvim-lspconfig). Using
[nvim-lspconfig](https:#github.com/neovim/nvim-lspconfig). Using
`nvim-lspconfig` setup `nls` like all your other LSP servers as described by the
`nvim-lspconfig` ReadMe.
@ -129,12 +129,12 @@ Add an `nickel_ls` entry to your configuration. Type `:CocConfig` in Neovim (or
```
{
"languageserver": {
// Your other language servers configuration
// ...,
# Your other language servers configuration
# ...,
"nickel_ls": {
"command": "nls",
// You can enable performance tracing with:
// "command": "nls --trace <file>",
# You can enable performance tracing with:
# "command": "nls --trace <file>",
"rootPatterns": [
".git"
],

View File

@ -1,13 +1,13 @@
### Using Makam
Makam is a dialect of lambda Prolog created and maintained by Antonis Stampoulis, more information [here](https://github.com/astampoulis/makam).
Makam is a dialect of lambda Prolog created and maintained by Antonis Stampoulis, more information [here](https:#github.com/astampoulis/makam).
This is an attempt to use it to define the semantics of the **Nickel** language.
He distributes it through [NPM](https://www.npmjs.com/package/makam), and on the [repo](https://github.com/astampoulis/makam) there's information on how to get it working.
He distributes it through [NPM](https:#www.npmjs.com/package/makam), and on the [repo](https:#github.com/astampoulis/makam) there's information on how to get it working.
#### Using it on Nix
We use the `node2nix` helper, there's a [PR](https://github.com/NixOS/nixpkgs/pull/67703) to add it to nixpkgs, but for now this simple config should help.
We use the `node2nix` helper, there's a [PR](https:#github.com/NixOS/nixpkgs/pull/67703) to add it to nixpkgs, but for now this simple config should help.
Just `nix-build -A makam` and then `result/bin/makam ./src/init.makam -`. Or run `result/bin/makam src/init.makam src/examples.makam` to run the examples.

View File

@ -10,10 +10,10 @@ This document is a proposal for an overriding mechanism in Nickel. It is
expected to evolve while we put these ideas in practice, but shall serve as
a design and implementation baseline.
Related issues: [#103](https://github.com/tweag/nickel/issues/103),
[#240](https://github.com/tweag/nickel/issues/240),
[#255](https://github.com/tweag/nickel/issues/255),
[#279](https://github.com/tweag/nickel/issues/279).
Related issues: [#103](https:#github.com/tweag/nickel/issues/103),
[#240](https:#github.com/tweag/nickel/issues/240),
[#255](https:#github.com/tweag/nickel/issues/255),
[#279](https:#github.com/tweag/nickel/issues/279).
## Context
@ -47,11 +47,11 @@ and returns a new updated record. It has the same semantics as our first
snippet, but doesn't require to rewrite all unchanged fields.
It can have a builtin syntax, such as OCaml's `with`: `{record with field =
new_value}`, Haskell's `record {field = newValue}`, Nix `//` operator `record //
new_value}`, Haskell's `record {field = newValue}`, Nix `#` operator `record #
{field = newValue}`, or Rust's syntax `RecordDataType {field: new_value,
..record}`. There are more advanced programming techniques that make updating
deeply nested records ergonomic such as
[Lenses](https://www.fpcomplete.com/haskell/tutorial/lens/) in Haskell, but
[Lenses](https:#www.fpcomplete.com/haskell/tutorial/lens/) in Haskell, but
these rely too heavily on advanced language and typing features to be practical
in Nickel.
@ -73,7 +73,7 @@ As explained in the next section though, this is not satisfying.
Nickel's records are different from the ones of OCaml, Haskell or Rust. They
are lazy and recursive by default. They are thus better understood as
[codata](https://link.springer.com/chapter/10.1007/978-3-030-17184-1_5)
[codata](https:#link.springer.com/chapter/10.1007/978-3-030-17184-1_5)
rather than data. Take the following example:
```nickel
@ -136,7 +136,7 @@ having it implemented in user code leads to some general well-known issues:
error reporting much harder.
- It is potentially harder to make user land implementations efficient.
See [this gist](https://gist.github.com/edolstra/29ce9d8ea399b703a7023073b0dbc00d)
See [this gist](https:#gist.github.com/edolstra/29ce9d8ea399b703a7023073b0dbc00d)
for more details. We continue with an overview of existing mechanisms in Nix and
related languages.
@ -158,7 +158,7 @@ rRepr = self: {
```
`self` is a self-reference, akin to `this` in object oriented languages. It is
computed as a [fixpoint](https://en.wikipedia.org/wiki/Fixed-point_combinator),
computed as a [fixpoint](https:#en.wikipedia.org/wiki/Fixed-point_combinator),
simply realized by auto-application in Nix, thanks to laziness:
```nix
@ -173,15 +173,15 @@ the original representation:
```nix
let extension = {a = 2;}; in
# The fixpoint of result is { a = 2; b = 3; }
resultRepr = self: (rRepr (self // extension)) // extension
resultRepr = self: (rRepr (self # extension)) # extension
```
The second outer update ensures that the final result is also set to `a = 2`,
and not only the `a` appearing in `b`.
Some details are left out, but this is the gist of it. See also [the Nix pill on
overriding](https://nixos.org/guides/nix-pills/override-design-pattern.html) or
[this article on fixpoints in Nix](http://r6.ca/blog/20140422T142911Z.html).
overriding](https:#nixos.org/guides/nix-pills/override-design-pattern.html) or
[this article on fixpoints in Nix](http:#r6.ca/blog/20140422T142911Z.html).
#### Limits
@ -195,14 +195,14 @@ overriding](https://nixos.org/guides/nix-pills/override-design-pattern.html) or
a = {b = self.a.c;};
}; in
let extension = {a = {c = 2;};}; in
rExt = let fixpoint = rRepr (fixpoint // extension); in
fixpoint // extension
rExt = let fixpoint = rRepr (fixpoint # extension); in
fixpoint # extension
# Gives {a = {c = 2;};} instead of expected {a = {b = 2; c = 2;};}
```
### Nixpkgs overlays
[Overlays](https://nixos.wiki/wiki/Overlays) can be seen as a sequence of
[Overlays](https:#nixos.wiki/wiki/Overlays) can be seen as a sequence of
transformations from a base record, each layer having access to a `super`
reference to the previous layer and the `self` reference to the final value.
@ -217,13 +217,13 @@ let overlay1 = self: super: {a = 1;}; in
let overlay2 = self: super: {b = 1; a = super.a + 1;}; in
let applyOverlays = self:
let base = baseRepr self; in
let first = base // overlay1 self base; in
let second = first // overlay2 self first; in
let first = base # overlay1 self base; in
let second = first # overlay2 self first; in
second; in
let fixpoint = applyOverlays fixpoint; in fixpoint
```
In practice, the `super // ..` and fixpoints parts can be factorised in
In practice, the `super # ..` and fixpoints parts can be factorised in
dedicated helper functions.
#### Advantages
@ -243,7 +243,7 @@ dedicated helper functions.
- ~~**(NEST)**~~ Overriding nested fields is still clumsy. For example, to
override `lib.firefoxVersion`:
```nix
self: super: { lib = (super.lib or {}) // { firefoxVersion = ...; }; }
self: super: { lib = (super.lib or {}) # { firefoxVersion = ...; }; }
```
### NixOs module system
@ -326,7 +326,7 @@ fields: {
}
```
Combined with [default values](https://cuelang.org/docs/tutorials/tour/types/defaults/),
Combined with [default values](https:#cuelang.org/docs/tutorials/tour/types/defaults/),
this provides an overriding mechanism:
```
@ -378,7 +378,7 @@ local obj = {
]
```
This is similar to the Nix operator `//`, but doing recursive overriding in the
This is similar to the Nix operator `#`, but doing recursive overriding in the
expected way out of the box. The extension can access the previous version in
the same way as Nixpkgs overlays, using the `super` keyword.
@ -408,18 +408,18 @@ strikingly resemble the semantics of objects and classes in OOP. Replace
records with objects, fields with methods and overriding with inheritance. This
is not so surprising: there's actually an history of encoding objects in
functional languages as recursive records (see the introduction of
[The Recursive Record Semantics of Objects Revisited](https://hal.inria.fr/inria-00072423)
[The Recursive Record Semantics of Objects Revisited](https:#hal.inria.fr/inria-00072423)
for a good overview) going back to 1988
\[[Cardelli](http://lucacardelli.name/papers/inheritance.pdf)\].
\[[Cardelli](http:#lucacardelli.name/papers/inheritance.pdf)\].
This is also mentioned in the
[README](https://github.com/MuKnIO/nixpkgs/blob/devel/lib/pop.md#some-historical-context)
[README](https:#github.com/MuKnIO/nixpkgs/blob/devel/lib/pop.md#some-historical-context)
of POP (an object system in Nix), where the author observes that overriding
mechanisms in Nix (and Jsonnet for that matter) are a simplified lazy object
system (simplified because objects lack proper state and there is no distinction
between classes and instances). Their logical conclusion is to embrace this fact
and design a proper object system helped by existing literature, rather than
reinventing the wheel. Similarly, Nix [overlays](https://nixos.wiki/wiki/Overlays)
reinventing the wheel. Similarly, Nix [overlays](https:#nixos.wiki/wiki/Overlays)
can be seen as a single inheritance mechanism.
Inheritance-based overriding imposes an order on the overrides. A single level
@ -430,7 +430,7 @@ The NixOS module system is designed differently. It is based on merging: the
configuration is created by combining a set of unordered records following
specific rules. Of course, there's still a need for ordering information
somewhere, but it is rather expressed as priorities. This system has the
advantage of making merge commutative (in contrast with inheritance or the `//`
advantage of making merge commutative (in contrast with inheritance or the `#`
operator), as in CUE, and to untie data definition from precedence
specification: one can define a module where each field has a different
priority, if it makes sense to group them logically. With inheritance, values
@ -486,7 +486,7 @@ r = {
a = 1;
b = a + 1;
}
// Definition of the representation of r
# Definition of the representation of r
repr(r) := fun self => {
a = 1;
b = self.a + 1;
@ -550,7 +550,7 @@ let block2 = {
path = ["/bin"]
} in
// { path = ["usr/local/bin", "/bin"] }
# { path = ["usr/local/bin", "/bin"] }
block1 & block2
```
@ -584,7 +584,7 @@ expression again.
There's more potential optimizations, but this first step should be a reasonable
trade-off between implementation complexity and performance. See
[#103](https://github.com/tweag/nickel/issues/103) for more details.
[#103](https:#github.com/tweag/nickel/issues/103) for more details.
### Scoping
@ -640,7 +640,7 @@ Example:
| default = 1,
bar | Str,
//equivalent to `bar | Str | priority 0`
#equivalent to `bar | Str | priority 0`
baz.boo.bor | priority -4 = "value",
@ -650,13 +650,13 @@ Example:
#### Recursive priorities
As noted in [#240](https://github.com/tweag/nickel/issues/240), configurations
As noted in [#240](https:#github.com/tweag/nickel/issues/240), configurations
should be easily overridable, and the approach outlined until now can end up
annoyingly requiring configurations to be written with either `default` or
`force` everywhere.
This RFC proposes to add *recursive* (or "leafy", or "push down") priorities, as
described in [#279](https://github.com/tweag/nickel/issues/279). We define the
described in [#279](https:#github.com/tweag/nickel/issues/279). We define the
new meta-values `default rec` and `force rec`, whose semantics are defined as:
- `eval(expr | default rec)`: case of `eval(expr)`:
@ -688,31 +688,31 @@ let neutralConf = {
}
let defaulted | default rec = neutralConf
// ^ Will evaluate to:
// {
// foo | default = 1,
// bar = {
// baz | default = "stuff",
// bar.blorg | default = false,
// },
// }
// This is different from `neutralConf | default`! The latter version
// would be overrided at once, as illustrated below.
# ^ Will evaluate to:
# {
# foo | default = 1,
# bar = {
# baz | default = "stuff",
# bar.blorg | default = false,
# },
# }
# This is different from `neutralConf | default`! The latter version
# would be overrided at once, as illustrated below.
defaulted & {bar.baz = "shapoinkl"}
// ^ Gives the expected:
// {
// foo | default = 1,
// bar = {
// baz = "shapoinkl";
// bar.blor | default = false,
// },
// }
// While
# ^ Gives the expected:
# {
# foo | default = 1,
# bar = {
# baz = "shapoinkl";
# bar.blor | default = false,
# },
# }
# While
(neutralConf | default) & {bar.baz = "shapoinkl"}
// ^ This gives only:
// {bar.baz = "shapoinkl"}
# ^ This gives only:
# {bar.baz = "shapoinkl"}
```
This way, an existing definition (arbitrarily complex: that could be the root of
@ -734,7 +734,7 @@ definition:
```nickel
let add = fun args => args.lower + args.higher in
// {a = 3}
# {a = 3}
{a | merge add = 1} & {a = 1} & {a = 1}
```
@ -746,10 +746,10 @@ let r1 = {a = 1} in
let r2 = {a = 1} in
let r3 = {a | merge add = 1} in
// {val = 2}
# {val = 2}
r1 & r2 & r3
// {val = 3}
# {val = 3}
r3 & r1 & r2
```
@ -770,7 +770,7 @@ Possible solutions:
val1 & val2 & (val3 | merge func)
<=> (val1 | merge func) & val2 & val3
<=> func (func val1 val2) val3
// instead of the naive
# instead of the naive
<=/=> func (val1 & val2) val3
```
@ -824,17 +824,17 @@ questions:
let add = fun x y => x + y in
let var = 1 & 1 in
var & (1 | merge add) // result?
(var | merge add) & 1 // result?
var & (1 | merge add) # result?
(var | merge add) & 1 # result?
((1 & 1) + (1 & 1)) & (1 | merge add) // result?
((1 & 1) + (1 & 1) | merge add) & 1 // result?
((1 & 1) + (1 & 1)) & (1 | merge add) # result?
((1 & 1) + (1 & 1) | merge add) & 1 # result?
// file: somefile.ncl
# file: somefile.ncl
1 & 1
// file: other.ncl
(import "somefile") & (1 | merge add) // result?
(import "somefile" | merge add) & 1 // result?
# file: other.ncl
(import "somefile") & (1 | merge add) # result?
(import "somefile" | merge add) & 1 # result?
```
In the following, we will write "meta"-code (think of the code of the Nickel
@ -857,7 +857,7 @@ data Metadata = Metadata {
priority :: Priority,
merge :: Option Priority,
contracts :: Option (List Contract),
// ...
# ...
}
data AbsMergeTree a =
@ -873,7 +873,7 @@ expressions, such that `let x = a & b in x & c` and `a & b & c` has the same
merge tree: that is, merge treee commute with evaluation.
```
// we maintain an environment of bindings in `env`
# we maintain an environment of bindings in `env`
metaData [| e | attr = val, ... |] ::=
{ attr = val, ... } if priority is set
@ -883,21 +883,21 @@ mergeTree e ::= (absMergeTree e, metaData e)
absMergeTree [| e1 & e2 |] = Merge(mergeTree(e1), mergeTree(e2))
// whnf = Weak head normal form, result of evaluation
# whnf = Weak head normal form, result of evaluation
absMergeTree [| whnf |] @ e = Exp(e)
// Should mergeTree cross import boundaries? Probably not
# Should mergeTree cross import boundaries? Probably not
absMergeTree [| import path |] @ e = Exp(e)
// All other cases
# All other cases
absMergeTree e = weakEval e
// weakEval is defined exactly as standard evaluation, excepted that it stops at
// merge expressions, as if they were a lazy datatype in weak head normal form
# weakEval is defined exactly as standard evaluation, excepted that it stops at
# merge expressions, as if they were a lazy datatype in weak head normal form
weakEval [| e1 & e2 |] @ e = e
// all other cases are defined exactly as for eval
# all other cases are defined exactly as for eval
weakEval e = ...
```
@ -920,7 +920,7 @@ extractMergeFuns (Leaf(e,meta)) = [f] if meta.merge == Some([| f |])
mergeFunction : MergeTree -> Result MergeFunction ()
mergeFunction t = let funs = extractMergeFuns t in
if lists.length t == 0 then
Ok(__builtinMerge) // the standard `&` merge function
Ok(__builtinMerge) # the standard `&` merge function
else if lists.length t == 1 then
Ok(head t)
else
@ -930,7 +930,7 @@ mergeFunction t = let funs = extractMergeFuns t in
And the interpretation of a merge tree by a merge function:
```
// can be extended, as long as it is a partially ordered set
# can be extended, as long as it is a partially ordered set
Priority = Number | -inf | +inf | ...
interpret (Merge(ast_1,meta_1),Merge(ast_2,meta_2)) f =

View File

@ -189,7 +189,7 @@ clarification:
to take it into account. But this implies giving semantics to things like
`record.is_empty { ; a}` or `{ ; a} & { ; b}`. Those are actually
interesting questions outside of the context of this RFC (see
[201](https://github.com/tweag/nickel/issues/201)).
[201](https:#github.com/tweag/nickel/issues/201)).
While allowing tails in arbitrary record may look more uniform, it requires
more design thinking. There isn't any obvious practical usage that seems to
@ -434,9 +434,9 @@ idea from the [Record types](#record-types) section:
```nickel
let Contract = {foo | OtherContr, bar | Str} in
let my_value | Contract = {...} in
// Would typecheck, as my_value has type Contract, which would be expanded to
// {foo | OtherContr, bar | Str} and statically extracted as {foo: OtherContr, bar:
// Str}
# Would typecheck, as my_value has type Contract, which would be expanded to
# {foo | OtherContr, bar | Str} and statically extracted as {foo: OtherContr, bar:
# Str}
let appendToBar : Str -> Str = fun s => my_value.bar ++ s
```