* New language feature: with expressions.

The expression `with E1; E2' evaluates to E2 with all bindings in
  the attribute set E1 substituted.  E.g.,

    with {x = 123;}; x

  evaluates to 123.  That is, the attribute set E1 is in scope in E2.

  This is particularly useful when importing files containing lots
  definitions.  E.g., instead of

    let {
      inherit (import ./foo.nix) a b c d e f;

      body = ... a ... f ...;
    }

  we can now say

    with import ./foo.nix;

    ... a ... f ...

  I.e., we don't have to say what variables should be brought into scope.
This commit is contained in:
Eelco Dolstra 2004-10-25 16:54:56 +00:00
parent f4d44a0026
commit 37d7abd694
7 changed files with 61 additions and 27 deletions

View file

@ -27,7 +27,7 @@ sub createLinks {
$srcFile =~ /\/nix-support$/ || $srcFile =~ /\/nix-support$/ ||
$srcFile =~ /\/log$/) $srcFile =~ /\/log$/)
{ {
# Do noting. # Do nothing.
} }
elsif (-d $srcFile) { elsif (-d $srcFile) {

View file

@ -17,18 +17,12 @@ available for installation.</para>
<para>In Nix, different users can have different <quote>views</quote> <para>In Nix, different users can have different <quote>views</quote>
on the set of installed applications. That is, there might be lots of on the set of installed applications. That is, there might be lots of
applications present on the system (possibly in many different applications present on the system (possibly in many different
versions), but users can have a specific selection of those versions), but users can have a specific selection of those active —
active — where <quote>active</quote> just means that it appears where <quote>active</quote> just means that it appears in a directory
in a directory in the user's <envar>PATH</envar>.</para> in the user's <envar>PATH</envar>. Such a view on the set of
installed applications is called a <emphasis>user
<para>Such a view on the set of installed applications is called a environment</emphasis>, which is just a directory tree consisting of
<emphasis>user environment</emphasis>, which is just a directory tree symlinks to the files of the active applications. </para>
consisting of symlinks to the files of the active applications. In
Nix, operations such as upgrading or removing components never
overwrite or remove the files of those components, and they don't even
touch the user environments that point to them. Rather, they cause a
<emphasis>new</emphasis> user environment to be constructed based on
the old one.</para>
<para>Components are installed from a set of <emphasis>Nix <para>Components are installed from a set of <emphasis>Nix
expressions</emphasis> that tell Nix how to build those components, expressions</emphasis> that tell Nix how to build those components,
@ -168,7 +162,21 @@ set.</para></footnote></para>
<sect1><title>Profiles</title> <sect1><title>Profiles</title>
<para>Bla</para> <para>In Nix, operations such as upgrading or removing components
never overwrite or remove the files of those components, and they
don't even touch the user environments that point to them. Rather,
they cause a <emphasis>new</emphasis> user environment to be
constructed based on the old one. This is illustrated in Figure
bla.</para>
<figure><title>User environments</title>
<mediaobject>
<imageobject>
<imagedata fileref='figures/userenv-frame3.png' format='PNG' />
</imageobject>
</mediaobject>
</figure>
</sect1> </sect1>

View file

@ -230,7 +230,7 @@ Expr evalExpr2(EvalState & state, Expr e)
try { try {
return evalExpr(state, substArgs(e4, formals, e2)); return evalExpr(state, substArgs(e4, formals, e2));
} catch (Error & e) { } catch (Error & e) {
throw Error(format("while evaluating function at %1%:\n%2%") throw Error(format("while evaluating the function at %1%:\n%2%")
% showPos(pos) % e.msg()); % showPos(pos) % e.msg());
} }
} }
@ -241,7 +241,7 @@ Expr evalExpr2(EvalState & state, Expr e)
subs.set(name, e2); subs.set(name, e2);
return evalExpr(state, substitute(subs, e4)); return evalExpr(state, substitute(subs, e4));
} catch (Error & e) { } catch (Error & e) {
throw Error(format("while evaluating function at %1%:\n%2%") throw Error(format("while evaluating the function at %1%:\n%2%")
% showPos(pos) % e.msg()); % showPos(pos) % e.msg());
} }
} }
@ -258,7 +258,7 @@ Expr evalExpr2(EvalState & state, Expr e)
try { try {
return evalExpr(state, a); return evalExpr(state, a);
} catch (Error & e) { } catch (Error & e) {
throw Error(format("while evaluating attribute `%1%' at %2%:\n%3%") throw Error(format("while evaluating the attribute `%1%' at %2%:\n%3%")
% s1 % showPos(pos) % e.msg()); % s1 % showPos(pos) % e.msg());
} }
} }
@ -283,6 +283,26 @@ Expr evalExpr2(EvalState & state, Expr e)
return evalExpr(state, e2); return evalExpr(state, e2);
} }
/* Withs. */
if (atMatch(m, e) >> "With" >> e1 >> e2 >> pos) {
ATermMap attrs;
try {
e1 = evalExpr(state, e1);
queryAllAttrs(e1, attrs);
} catch (Error & e) {
throw Error(format("while evaluating the `with' definitions at %1%:\n%2%")
% showPos(pos) % e.msg());
}
try {
e2 = substitute(attrs, e2);
checkVarDefs(state.primOps, e2);
return evalExpr(state, e2);
} catch (Error & e) {
throw Error(format("while evaluating the `with' body at %1%:\n%2%")
% showPos(pos) % e.msg());
}
}
/* Generic equality. */ /* Generic equality. */
if (atMatch(m, e) >> "OpEq" >> e1 >> e2) if (atMatch(m, e) >> "OpEq" >> e1 >> e2)
return makeBool(evalExpr(state, e1) == evalExpr(state, e2)); return makeBool(evalExpr(state, e1) == evalExpr(state, e2));
@ -357,7 +377,7 @@ Expr evalFile(EvalState & state, const Path & path)
try { try {
return evalExpr(state, e); return evalExpr(state, e);
} catch (Error & e) { } catch (Error & e) {
throw Error(format("while evaluating file `%1%':\n%2%") throw Error(format("while evaluating the file `%1%':\n%2%")
% path % e.msg()); % path % e.msg());
} }
} }

View file

@ -48,6 +48,7 @@ if { return IF; }
then { return THEN; } then { return THEN; }
else { return ELSE; } else { return ELSE; }
assert { return ASSERT; } assert { return ASSERT; }
with { return WITH; }
let { return LET; } let { return LET; }
rec { return REC; } rec { return REC; }
inherit { return INHERIT; } inherit { return INHERIT; }

View file

@ -296,7 +296,7 @@ void checkVarDefs(const ATermMap & defs, Expr e)
ATMatcher m; ATMatcher m;
ATerm name; ATerm name;
ATermList formals; ATermList formals;
ATerm body; ATerm with, body;
ATermList rbnds, nrbnds; ATermList rbnds, nrbnds;
if (atMatch(m, e) >> "Var" >> name) { if (atMatch(m, e) >> "Var" >> name) {
@ -340,6 +340,13 @@ void checkVarDefs(const ATermMap & defs, Expr e)
} }
checkVarDefs(defs2, (ATerm) rbnds); checkVarDefs(defs2, (ATerm) rbnds);
} }
else if (atMatch(m, e) >> "With" >> with >> body) {
/* We can't check the body without evaluating the definitions
(which is an arbitrary expression), so we don't do that
here but only when actually evaluating the `with'. */
checkVarDefs(defs, with);
}
else if (ATgetType(e) == AT_APPL) { else if (ATgetType(e) == AT_APPL) {
int arity = ATgetArity(ATgetAFun(e)); int arity = ATgetArity(ATgetAFun(e));

View file

@ -41,11 +41,11 @@ ATerm makePos(YYLTYPE * loc, void * data)
ATermList ts; ATermList ts;
} }
%type <t> start expr expr_function expr_assert expr_if expr_op %type <t> start expr expr_function expr_if expr_op
%type <t> expr_app expr_select expr_simple bind inheritsrc formal %type <t> expr_app expr_select expr_simple bind inheritsrc formal
%type <ts> binds ids expr_list formals %type <ts> binds ids expr_list formals
%token <t> ID INT STR PATH URI %token <t> ID INT STR PATH URI
%token IF THEN ELSE ASSERT LET REC INHERIT EQ NEQ AND OR IMPL %token IF THEN ELSE ASSERT WITH LET REC INHERIT EQ NEQ AND OR IMPL
%nonassoc IMPL %nonassoc IMPL
%left OR %left OR
@ -67,12 +67,10 @@ expr_function
{ $$ = ATmake("Function(<term>, <term>, <term>)", $2, $5, CUR_POS); } { $$ = ATmake("Function(<term>, <term>, <term>)", $2, $5, CUR_POS); }
| ID ':' expr_function | ID ':' expr_function
{ $$ = ATmake("Function1(<term>, <term>, <term>)", $1, $3, CUR_POS); } { $$ = ATmake("Function1(<term>, <term>, <term>)", $1, $3, CUR_POS); }
| expr_assert | ASSERT expr ';' expr_function
;
expr_assert
: ASSERT expr ';' expr_assert
{ $$ = ATmake("Assert(<term>, <term>, <term>)", $2, $4, CUR_POS); } { $$ = ATmake("Assert(<term>, <term>, <term>)", $2, $4, CUR_POS); }
| WITH expr ';' expr_function
{ $$ = ATmake("With(<term>, <term>, <term>)", $2, $4, CUR_POS); }
| expr_if | expr_if
; ;

View file

@ -205,7 +205,7 @@ static Expr primDerivation(EvalState & state, const ATermVector & _args)
try { try {
processBinding(state, value, ne, ss); processBinding(state, value, ne, ss);
} catch (Error & e) { } catch (Error & e) {
throw Error(format("while processing derivation attribute `%1%' at %2%:\n%3%") throw Error(format("while processing the derivation attribute `%1%' at %2%:\n%3%")
% key % showPos(pos) % e.msg()); % key % showPos(pos) % e.msg());
} }