-
Notifications
You must be signed in to change notification settings - Fork 3
Lexical Scope
Khepri uses lexical scoping and encourages the use of immutable bindings.
Four main elements introduce a new lexical scope: programs, packages, function bodies, and block statements
// Annotated to show which variables are in scope
// a, b
var a = 10;
var b;
if (a > 0)
{ // a, b, g, f
var g = 100;
var f -> \a -> { var z = 13; return a - g; } // a, b, g, f, a, z
b = f(3);
}
Implicit blocks also introduce a new scope, such as if
or for
statements without brackets:
var h = "hello";
if (h)
var w = "world";
var message = h + " " + w; // error, w used outside of implicit if block.
Function bodies and with
statement use the same block as their parameters or bindings:
\x -> {
var x = 10; // Error: x already bound in scope.
};
Variables declarations are evaluated in order, and only previously declared variables can be used.
var h = "hello";
var message = h + " " + w; // error, w used before declared.
var w = "world";
Duplicate variables in the same scope are disallowed:
var b = 3;
var c = b;
var b = c; // Error, b already declared.
Similarly, duplicate parameter names are disallowed:
\x, y, x -> x + y; // error, x defined twice.
Only variables declared in a variable declaration with =
and package exports have mutable bindings, meaning they can be reassigned. Those from static declarations, catch clauses, function parameters, and let expressions are immutable cannot be reassigned.
var a = 4;
a = 10; // ok;
static g;
g = 323; // Error, g is immutable
var f = \x -> { x = 3; return x + 1; }; // error, x is immutable
A previously mutable binding can be marked immutable with a :=
assignment expression. This is the recommended practice for package exports as it can catch programmer errors, allows inlining, and better documents the package exports.
var x = 2;
x = 4;
x := 10;
x = 5; // error
A binding may only be marked immutable in the same scope in which it was declared.
var x = 2;
{
x := 10; // Error, not in same scope.
}
if (x > 0) x := 10; // And error here too.
for (x := 10 ; ; ) ... // And here.
Additionally, a binding my only be marked immutable if it was only mutated in the current scope.
var x = 2;
x = 3;
if (x)
{
f(x);
}
x := 10; // ok, x only mutated in current scope.
var x = 2;
if (x)
{
x = 3;
}
x := 10; // error, x previously mutated in enclosed scope..
Marking bindings immutable also works with chaining
var x, y, z;
// `x` and `z` are now immutable, but not `y`.
x := y = z := 10;
You can declare the existence of a global variable using a static
variable declarations. Builtin globals are already declared, but this does not include any DOM objects.
// declare that `define` is a variable in the global scope
static define;
define([], \() -> {
var props = {'x': {'value': 3}};
return Object.keys({}, props); // 'Object' builtin ok even without explicit static.
});
static
only suppresses checks on the global variables, it does not effect the behavior of the program.
Use of a global can also be restricted to a block. This may help make intent clearer but does not change the meaning of the actual program
var a = \x -> {
static $;
return $('<div></div>');
}