Metaprogramming
siteId — identifying a lambda in the source
Every reified lambda literal gets a number that is fixed at compile time, distinct for each lambda in the source, and the same on every run.
since 0.1.0-alpha.1linuxwindowswasm
Description
Expr.siteId is an int that identifies the lambda literal the Expr was built from, not the
moment it was built. The compiler assigns the numbers while it checks the program: the first reified
lambda gets 0, the next 1, and so on, in the order the lambdas appear in the source file.
Because the number belongs to the place in the source, a lambda inside a function that is called many
times has the same siteId on every call. Two different lambdas, even with identical text, have different
ids. The number is fixed at compile time. It involves no hash of the source, no clock and no randomness,
so the same program compiled again gets the same ids.
A library can use siteId as a cache key: a query builder that translates a lambda to SQL can remember
the translation under its siteId and skip the work the next time the same lambda is reached. The
language does not cache anything itself. Keeping such a cache is the consumer's job.
One id per lambda in the source
class User {
int age;
new User(int a) { age = a; }
}
expr::Expr<(User) => bool> first = (u) => u.age > 1;
expr::Expr<(User) => bool> second = (u) => u.age > 1;
expr::Expr<(User) => bool> make() {
return (u) => u.age > 1;
}
expr::Expr<(User) => bool> third = make();
expr::Expr<(User) => bool> fourth = make();
console.writeln(first.siteId);
console.writeln(second.siteId);
console.writeln(first.siteId != second.siteId);
console.writeln(third.siteId == fourth.siteId);
0
1
true
true
Rules
- A
siteIdis unique among the reified lambdas of one compiled program and starts at0. - Evaluating the same lambda literal again, for example by calling the function that contains it again,
produces an
Exprwith the samesiteId. - The numbers follow source order, but a program should treat them as opaque identifiers and compare them only for equality.
Examples
A cache keyed by siteId
class User {
int age;
new User(int a) { age = a; }
}
Map<int, string> cache = Map();
int translations = 0;
string translate(expr::Expr<(User) => bool> e) {
if (cache.has(e.siteId)) {
return cache.at(e.siteId);
}
translations = translations + 1;
string sql = "translation ${translations}";
cache[e.siteId] = sql;
return sql;
}
void lookup() {
console.writeln(translate((u) => u.age > 18));
}
lookup();
lookup();
console.writeln(translate((u) => u.age > 21));
translation 1
translation 1
translation 2
See also
- Expression reification — lambdas as data — A lambda literal in a position typed expr::Expr<F> compiles to an ordinary closure plus a walkable tree of its checked body, which is what query builders translate to other languages.
- expr — The expression-reification tree: runtime, walkable descriptions of lambda bodies.