Functions

Matt Bierner edited this page May 13, 2014 · 21 revisions

Khepri's function syntax is less verbose and more expressive than the standard ECMAScript function syntax. A lambda syntax allows directly returning the result of an expression and functions can unpack argument values and the this object in the parameter list.

Khepri does not support function declarations, all functions are expressions.

Function Syntax

A basic function is declared with a \ followed by a list of zero or more unpacks. -> separates the argument list from the function body, which may be either an expression or a block statement.

// single argument lambda function
\x -> x + 3;

// multiple argument lambda function, commas are optional
\x y -> x + y;
\x, y -> x + y;

// No arguments
\ -> 3;

The body of a function can be a block of statement that returns a value.

\x y -> {
    var z = x + 10;
    return z + y;
};

Lambda functions that return an object literal must wrap the object literal in parentheses:

\x -> ({'x': x});

Functions are right associative

\x -> \y -> x / y;
// is the same as
\x -> (\y -> x / y);

Limiting capture

Lambda function bodies capture as much as possible. There are two ways to limit this behavior.

Wrap the entire function in parenthesis:

// One use case is composing with a lambda instead of composing with its body.
// This is really
\x -> ... \> g
\x -> (... \> g);

// Using parenthesis:
(\x -> ...) \> g

Or end the lambda function body with §:

\x -> ... § \> g
(\x -> ...) \> g

// Useful for chaining on an object
SomeObject
    .chain \ x -> ... §
    .chain \ y -> ... §
    .chain \ z -> ... ;

Named Functions

A function can be named by prefixing the declaration with the function keyword, followed by an optional identifier name.

// Named function expressions
function f \x -> ?x < 10 : f(x + 1) : x;

Using function without giving a name is also valid:

var f = function \x -> ?x < 10 : f(x + 1) : x;

Like ECMAScript functions, the function name is only in scope inside the body of the function:

var z = function f \x -> ?x < 10 : f(x + 1) : x;
f(); // error, f not in scope

Arguments Pattern

Function parameters specify arbitrary unpacks for the function's arguments. Besides the examples given above, function syntax also allows explicitly unpacking the arguments object.

The arguments magic identifier is not valid in Khepri and should not be used.

Implicit Arguments Pattern

Implicit form is a list (optionally comma separated) of zero or more patterns. This is the standard function syntax. Each pattern unpacks the argument value at its index.

var f = \x y {z} -> x + y + z;
f(1, 3, {'z': 5}); // 9

Explicit

The explicit arguments unpacks also allows unpacking the arguments object itself. After \, a - then an optional identifier pattern is followed by a parenthesized comma-optional list of patterns for the individual argument unpacks.

var f = \-args(...) -> args.length;
f(1, 2); // 2;
f(1, 2, 3, 4); // 4

Explicit form also allows grouping function parameter between parentheses. It is useful when the a function's arguments could be visually unclear, such as a function expression inside a function call.

static console;
console.log(1, 2, \-(x y z)-> x, 3, 4);

Argument Slices

A single slice unpack can be used anywhere in an argument list to unpack a range of arguments to an array.

var rest := \x ...xs -> xs;

rest(1, 2, 3, 4); // [2, 3, 4]

Regular parameters may appear both before and after the slice unpack. Parameters after the slice are taken relative to the end of the argument list. The slice unpack may collapse to contain zero elements:

var mid := \first ...mid last -> mid;

mid(1, 2, 3, 4); // [2, 3]
mid(1, 2); // []
mid(1); // []

An argument slice can be used to convert the arguments object to a regular array.

// This will always return a Javascript array of the arguments.
var args := \...a -> a;

args(1, 2, 3).map(+, 1); // [2, 3, 4]

Fat Arrows this Unpacks

Khepri remove the this expression (this is still a keyword) in favor of using explicit this bindings. The last element of an arguments pattern can optional be a this unpack of the form = PATTERN to unpack the this value a function is called with:

// Bind self to the `this` the function is called on.
\x =self-> self.z + x;

Fat arrow lexical scope the this object:

var Obj = function\ x =self-> { self.x = x; };
Obj.prototype.getXGetter = \=self-> \ -> self.x;
    
new Obj(3).getXGetter()(); 

A fat arrow self unpack may be any identifier pattern, as pattern, array pattern, or object pattern.

var Vec2 = function\x y =self-> { self.x = x; self.y = y; };

Vec2.prototype.scale = \s ={x y}-> new Vec2(s * x, s * x);

By simply adding or removing the =, you can switch between a function that takes the this object as an argument to a function that is called on the this object.

scaleThis = \s ={x y} -> new Vec2(s * x, s * x);
scaleArg  = \s  {x y} -> new Vec2(s * x, s * x);
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.