Skip to content

Operator To Function

mattbierner edited this page Sep 9, 2014 · 5 revisions

Most Khepri builtin operators, and all user defined operators, can be converted to a function by wrapping the operator in parentheses:

var add = (+);
add(1, 2); // 3

This allows operators to be passed directly to functions:

[1, 2, 3].reduce((+), 0); // 6

See dot expressions for creating partially applied accessor expressions such as:

var getX = (.x);
getX { x : 3}; // 3

Partial Application

As shorthand for partially applying a binary operator, you can include a curried argument in the parentheses. For all operators except binary . and new, the comma between the operator and the argument is optional

var add10 = (+ 10); // could also write (+, 10)
add10(2); // 12

Currying always curries the first arguments of the operator.

Application with Curried ops

Application and curried operators work fine, but have a syntax that may be confusing coming from Javascript.

var f = \x -> x;

// These are the same
f (+);
f ( (+) );

// The curried operator is always passed as the argument
f (+, 1);
f ( (+, 1) );

// You can change this behavior be wrapping the argument list in parens
f ((+), 1);

Flipped Binary Operators

You can flip a binary operator's arguments by prefixing the operator with _. Used with currying, this allows the second argument of a binary operator to be curried (think of _ as the placeholder where the function input will go).

var div10 = (_ / 10);

div10 60; // 6

For all ops except the keyword operators new and instanceof, the space between _ and the operator is optional.

(_ / 10); // no space needed

(_ new, 10); // Without space this becomes` (_new, 10)` where `_new` is an identifier.

Supported Operator

The following operators can be converted to functions (along with any user user defined operator derived from these operators).

Note that logical operators, like regular functions, do not use short-circuit evaluation once they are converted to functions.

Unary

  • typeof
  • void
  • ~
  • !
  • ++
  • --

Binary

  • instanceof
  • *
  • /
  • +
  • -
  • %
  • <<
  • >>
  • >>>
  • <
  • >
  • <=
  • >=
  • ==
  • ===
  • !=
  • !==
  • &&
  • ||
  • ??
  • |>
  • <|
  • \>
  • \>>
  • <\
  • <<\
  • . - Computed member a.(b)
  • @ - Binary curry only. a @ b
  • new - Binary version only. new a b

Ternary

  • ?

Output

Converting an operator to a function will add at most one declaration per operator to the header of the output file:

// Input
foldl @ (+) @ 0;
// Output
var __add = \x y -> x + y;
foldl.bind(null, __add, 0);

Uncurried operators therefore evaluate to simple object references, and can be used with very little overhead

// Input
// In the body of the for loop, `(+)` is just a reference and
// does not declare a new function every iteration
for (var i = 0; i < 10000; i = i + 1)
    f(i, (+));
// Output
var __add = \x y -> x + y;
for (var i = 0; i < 10000; i = i + 1)
    f(i, __add);

Multiple instances of a single, uncured operator will always reference the same function in a file, but not across multiple files. Although you can safely set properties or perform equality tests on converted operators within a file, this behavior is not recommended.

Inlining is used for converted operators, and converted operators that inlining makes unreachable will not be added to the header.

// Input ----
(+)(2, 3);
(+, 2);

// Output ----
6;
\y -> 2 + y;
Clone this wiki locally