* 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 =~ /\/log$/)
{
# Do noting.
# Do nothing.
}
elsif (-d $srcFile) {

View file

@ -17,18 +17,12 @@ available for installation.</para>
<para>In Nix, different users can have different <quote>views</quote>
on the set of installed applications. That is, there might be lots of
applications present on the system (possibly in many different
versions), but users can have a specific selection of those
active — where <quote>active</quote> just means that it appears
in a directory in the user's <envar>PATH</envar>.</para>
<para>Such a view on the set of installed applications is called a
<emphasis>user environment</emphasis>, which is just a directory tree
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>
versions), but users can have a specific selection of those active —
where <quote>active</quote> just means that it appears in a directory
in the user's <envar>PATH</envar>. Such a view on the set of
installed applications is called a <emphasis>user
environment</emphasis>, which is just a directory tree consisting of
symlinks to the files of the active applications. </para>
<para>Components are installed from a set of <emphasis>Nix
expressions</emphasis> that tell Nix how to build those components,
@ -168,7 +162,21 @@ set.</para></footnote></para>
<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>

View file

@ -230,7 +230,7 @@ Expr evalExpr2(EvalState & state, Expr e)
try {
return evalExpr(state, substArgs(e4, formals, e2));
} 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());
}
}
@ -241,7 +241,7 @@ Expr evalExpr2(EvalState & state, Expr e)
subs.set(name, e2);
return evalExpr(state, substitute(subs, e4));
} 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());
}
}
@ -258,7 +258,7 @@ Expr evalExpr2(EvalState & state, Expr e)
try {
return evalExpr(state, a);
} 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());
}
}
@ -283,6 +283,26 @@ Expr evalExpr2(EvalState & state, Expr e)
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. */
if (atMatch(m, e) >> "OpEq" >> e1 >> e2)
return makeBool(evalExpr(state, e1) == evalExpr(state, e2));
@ -357,7 +377,7 @@ Expr evalFile(EvalState & state, const Path & path)
try {
return evalExpr(state, 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());
}
}

View file

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

View file

@ -296,7 +296,7 @@ void checkVarDefs(const ATermMap & defs, Expr e)
ATMatcher m;
ATerm name;
ATermList formals;
ATerm body;
ATerm with, body;
ATermList rbnds, nrbnds;
if (atMatch(m, e) >> "Var" >> name) {
@ -341,6 +341,13 @@ void checkVarDefs(const ATermMap & defs, Expr e)
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) {
int arity = ATgetArity(ATgetAFun(e));
for (int i = 0; i < arity; ++i)

View file

@ -41,11 +41,11 @@ ATerm makePos(YYLTYPE * loc, void * data)
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 <ts> binds ids expr_list formals
%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
%left OR
@ -67,12 +67,10 @@ expr_function
{ $$ = ATmake("Function(<term>, <term>, <term>)", $2, $5, CUR_POS); }
| ID ':' expr_function
{ $$ = ATmake("Function1(<term>, <term>, <term>)", $1, $3, CUR_POS); }
| expr_assert
;
expr_assert
: ASSERT expr ';' expr_assert
| ASSERT expr ';' expr_function
{ $$ = ATmake("Assert(<term>, <term>, <term>)", $2, $4, CUR_POS); }
| WITH expr ';' expr_function
{ $$ = ATmake("With(<term>, <term>, <term>)", $2, $4, CUR_POS); }
| expr_if
;

View file

@ -205,7 +205,7 @@ static Expr primDerivation(EvalState & state, const ATermVector & _args)
try {
processBinding(state, value, ne, ss);
} 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());
}