Originally described in NixOS/rfcs#110 (comment)
{ pkgs, hello }:
let
with pkgs;
myHello = hello.override { ... };
in [
myHello
z3
] ++ let with pythonPackages; in [
z3
requests
]
Which gets desugared to:
{ pkgs, hello }:
let
inherit (pkgs) z3 pythonPackages;
myHello =
if pkgs ? myHello
then throw "myHello exists in pkgs, use a different variable name and verify its uses"
else hello.override { ... };
in [
myHello
z3
] ++ (let inherit (pythonPackages) z3 requests; in [
z3
requests
])
And refined in NixOS/rfcs#110 (comment):
The main problem was brought up on Discourse: That changes to one file could break the let with
in another file, for example:
Say you have
foo: bar:
let
with foo;
inherit bar;
in bar + baz
# desugars to
foo: _bar:
let
inherit (foo) baz;
bar =
if foo ? bar
then throw "bar exists in foo, use a different variable name and verify its uses"
else _bar;
in bar + baz
If you first write code and test it with a foo
that doesn't contain a bar
attribute it works. But in a next iteration it breaks because you run it with a foo
that does have a bar
. This is unexpected because whether a variable name is valid and usable should be known at parse-time, not eval-time.
Note that the alternative of desugaring to
foo: bar:
let
inherit (foo) baz;
inherit bar;
in bar + baz
has the problem that the user might've wanted to access foo.bar
, not the bar
in scope, and there's nothing warning them of that.
The only alternative I can see working is to use a warning instead of an error, this way you can't break code, so desugaring to this:
foo: _bar:
let
inherit (foo) baz;
bar =
if foo ? bar
then builtins.trace "warning: bar exists in foo, use a different variable name and verify its uses" _bar
else _bar;
in bar + baz
I don't see any problem with such a let with
idea: It's statically analyzable, can fully deprecate with
, warns you about any ambiguities, and is not overly verbose.
Forwarded from the matrix room:
Another question about the new syntax: I have see many "desurgaring" in the RFC. Are you expecting that it should be addressed in the parsing state, or
nix::Expr::bindVars
?