-
Notifications
You must be signed in to change notification settings - Fork 108
Description
Inherit
Nix has an inherit keyword that is used to define a field with the same name and value as a a variable in scope:
let foo = 1; in
{ inherit foo; }
# is equivalent to
{ foo = foo; }This kind of syntactic sugar is also present in other languages (Haskell, Rust, OCaml, etc.) sometimes under the name record /field punning.
Nix inherit also supports multiple fields, and the specification of another record to take the fields from:
let record = {foo = 1; bar = 2;}; in
let baz = 3; blarg = 4; in
{
inherit (record) foo bar;
inherit baz blarg;
}
# is equivalent to
{
foo = record.foo;
bar = record.bar;
baz = baz;
blarg = blarg;
}Recursive records in Nickel
While this is a nice shorthand, inherit is at most a nicety. However, in Nickel, records are recursive by default. This means that the expanded version above will just cause an infinite loop:
{foo = foo}This make defining fields with the same name and content as an identifier in scope a pain: one either has to change the variable from the beginning:
let foo_ = 1 in
let bar_ = 2 in
{
foo = foo_,
bar = bar_,
}But sometimes that is not desirable. Variables can be defined far away, and can be used in a lot of other places where it makes sense to keep the original name. The only solution left is to bind those variables to new ones locally, but this must be done before the definition of the record:
let foo = 1 in
let bar = 2 in
{
sub_record =
let foo = foo_ in
let bar = bar_ in
{
foo = foo_,
bar = bar_,
}
}Another case that I encountered in practice is when you need to recursively refer to a field defined in a parent record, but with the same name. There, you can't rename the original identifier either:
{
meta = "metadata",
sub = let meta_ = meta in {
meta = meta_
}
}This starts to be really painful. Because records are recursive by default, I think having field punning is even more important for Nickel than for other languages.
Describe the solution you'd like
Have a syntactic construct, which can be inherit or something else, to define fields with the same name and value as a list of identifiers in scope.
Note that the common punning syntax for several languages (Rust, OCaml, etc.) is to just write a field without a definition. For example in OCaml:
type r = {foo : int}
let foo = 1 in {foo;}Or in Rust:
struct Foo { bar: i32 }
fn mk_foo() -> Foo {
let bar = 1;
Foo {bar}
}But this solution doesn't cope well with the general syntax of Nickel.
{
foo | Num = 1, # a field with a contract and a definition
foo | Num, # a field with a contract but without definition
foo, # a field without a definition
}a lone field named foo, is taken to be a field without definitions, and this is consistent with other notations. Beside being a breaking change, I don't think that would be intuitive to change this syntax to mean inherit foo.