-
Notifications
You must be signed in to change notification settings - Fork 3
Functions
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.
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);
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 -> ... ;
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
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 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
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);
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]
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);