Function Operators
op
Creating an $OP. See $OP type.
Parameter Definition and Matching
Grapa's op
function supports flexible parameter definition and calling with both traditional programming syntax and JSON-style syntax.
Parameter Definition
/* Basic parameter definition */
f = op(a, b) { a + b; };
/* Parameters with default values */
f = op(a=1, b=2) { b**a; };
/* Mixed default and non-default parameters */
f = op(a, b=10, c) { a + b + c; };
Parameter Calling
Named Parameters (Traditional Syntax):
f = op(a=1, b=2) { b**a; };
f(a=3, b=4); /* → 64 */
f(b=5, a=2); /* → 25 */
Named Parameters (JSON-Style Syntax):
f = op(a=1, b=2) { b**a; };
f(a:3, b:4); /* → 64 */
f(b:5, a:2); /* → 25 */
Positional Parameters:
f = op(a=1, b=2) { b**a; };
f(3, 4); /* → 64 (a=3, b=4) */
f(5, 2); /* → 32 (a=5, b=2) */
Mixed Named and Positional:
f = op(a=1, b=2, c=3) { a + b + c; };
f(10, b:20); /* → 33 (a=10, b=20, c=3) */
f(c:30, 5); /* → 37 (a=5, b=2, c=30) */
Parameter Matching Rules
- Named Parameters: Can use either
=
or:
syntax - Positional Parameters: Assigned in order of definition
- Mixed Usage: Named parameters can be specified in any order, positional parameters fill remaining slots
- Default Values: Used when parameters are not provided
- JSON Compatibility:
:
syntax allows seamless integration with JSON objects
Examples
/* Complex parameter example */
calculator = op(operation="add", x=0, y=0) {
if (operation == "add") { x + y; }
else if (operation == "multiply") { x * y; }
else if (operation == "power") { x**y; }
else { 0; }
};
/* Various calling styles */
calculator(5, 3); /* → 8 (add) */
calculator(operation:"multiply", 5, 3); /* → 15 */
calculator(x:10, y:2, operation:"power"); /* → 100 */
calculator(operation="power", y:3, x:2); /* → 8 */
Parameter Behavior and Side Effects
Grapa uses pass-by-reference for all function parameters to maintain performance and avoid implementing a full garbage collection system. Default parameter values are immutable (each call gets a fresh copy), while passed parameters remain mutable.
Default Value Immutability
Default values are now immutable - each function call gets a fresh copy:
counter = op(count=0) {
count += 1;
count;
};
counter(); /* 1 */
counter(); /* 1 - default value is not modified */
Parameter Modification
Parameters can be modified within functions, affecting the original values:
increment = op(x) {
x += 1;
x;
};
value = 5;
increment(value); /* Returns 6 */
value; /* Now 6 */
When This Is Useful
This behavior is intentional and useful for: - Performance optimization - avoiding unnecessary copying - In-place operations - modifying data structures efficiently - C++-style programming - similar to pass-by-reference
When to Be Careful
Be cautious when: - Default values are immutable - each call gets a fresh copy - Passing shared data - modifications affect all references - Writing pure functions - avoid unintended side effects
Workaround for Value Copying
Use the .copy()
method or a copy function when you need to avoid side effects:
/* Method 1: Use .copy() method (recommended) */
data = [1, 2, 3];
process = op(arr) {
arr[0] = 999;
arr;
};
process(data.copy()); /* Returns [999, 2, 3] */
data; /* Still [1, 2, 3] - original unchanged */
/* Method 2: Copy function to force value copying */
copy = op(x) { x; };
data = [1, 2, 3];
process(copy(data)); /* Returns [999, 2, 3] */
data; /* Still [1, 2, 3] - original unchanged */
class
Creates a class that can be used to generate an instance of the class. The class definition is shared between all instances using the class. If information in the class is altered, a copy is made and the modified variable is added to the instance. The instance stores the class reference and any variables local to the instance. Classes can inherit 1 or more other classes.
myC = class {myV = 0; myF = op(a){myV=a};};
myC2 = class (myC) {myV2 = 0; myF2 = op(a){myV2=a};};
myIns = obj myC2;
@myIns;
{}
myIns.myF(4);
myIns.myV;
4
myIns.myV2;
0
myIns;
{"myV":4}
obj
Creating an instance of a class. See class.
token
Used to define lexical rules. Currently only used to define special character handling like space and tab, where the default handling is to skip these characters so they do not need to be specified in the grammar.
rule
See $RULE type
const
Sets a bit in the variable that prevents it from being changed. Will not be locked when accessed, which is useful for a global variable accssed by concurent threads.
x = const {one: "string one", two: "string two"};
Once set, the variable can not be modified. To turn off the const bit, use the following:
x.setconst(false);
x += (three:"th str");
x.setconst(true);
Note: const
is intended for performance (caching/optimization), not for locking or thread safety. It prevents modification unless setconst
is used, but does not synchronize access between threads. For explicit locking, see $thread.
static
Compile-time option. The code to construct the array (or value) is executed at compile time (during script loading), not at runtime. This reduces repeated computation and improves performance, but does not affect thread safety or immutability.
f = op(s)
{
x = static {one: "string one", two: "string two"};
x[s];
};
f("two");
Here is the compiled function with static:
@<[op,@[@<assign,{x,{"one":"string one","two":"string two"}}>,@<search,{@<var,{x}>,@<createlist,{@<var,{s}>}>}>]],{s}>
Here is the compiled funciton without static:
@<[op,@[@<assign,{x,@<prepend,{@<createlist,{@<name,{two,"string two"}>}>,@<name,{one,"string one"}>}>}>,@<search,{@<var,{x}>,@<createlist,{@<var,{s}>}>}>]],{s}>
If the list needs to be constructed using a variable that is passed in, than do not use the static operator and the variable will be constructed at run time.
Note: static
is for compile-time evaluation only. It does not make a value immutable or thread safe. For thread safety and locking, see $thread.
literal
Any sequence starting with an ascii letter, and including numbers and '_'.
literal ([params])
Assumes literal is a variable reference for $OP or $CODE, and runs the routine passing in parameters specified. See $OP and $CODE.
```