Skip to content
mattbierner edited this page Sep 23, 2014 · 10 revisions

Khepri uses lexical scoping and encourages the use of immutable bindings.

Blocks

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.
};

Declaration Order

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";

Rebinding

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.

Binding Mutability

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

Making a Mutable Binding 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;

Globals

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>');
}
Clone this wiki locally