Here's a proposal for adding "function" as a first-class data type in ASH.
I've put a fair amount of thought into it and see how I would find it useful - and how I could implement it - but now it's your turn to ponder it and offer suggestions and criticisms.
- What is missing that YOU would find useful?
- What is unclear or unspecified?
- What is wrong?
Please keep in mind that ASH is a strongly typed language and that function parameters and return values have types.
Please keep in mind that although I drew on both Java and Javascript for inspiration, this proposal is neither.
(Both of those languages have features that don't make sense in ASH - and ASH can allow things that neither allows.)
Please restrict suggestions to this specific aspect of ASH - the ability to use function objects in variables and parameters and invoke them with arguments.
I am aware, for example, that having GENERIC aggregates would make this even more useful.
It would be nice if ASH had a "list" type - and array (int[]) is sort of like one - but that is built on a Java array, rather than, say, an ArrayList, so you can't add or remove elements from it. (We could fix that by changing the internal implementation to ArrayList, which would not affect any existing programs, but which would add capabilities.)
Even so, I think this proposal would allow you to write things like:
MAP filter(MAP, PREDICATE)
MAP map(MAP, FUNCTION)
VALUE reduce(MAP, FUNCTION)
whereby you could accept a map variable, process its values, and produce another map or a reduced value.
Let's get going.
----
Here are a couple of functions:
What type are they?
What type is this?
What type is that?
I use typedefs in every ASH program I write - for aggregates, especially. I'm not sure I've seen them in anybody else's scripts, but I think they are incredibly useful - and equally so with function types. For clarity, I'm going to use the typedefs in further examples, but you don't HAVE to do that.
How can I declare some variables?
(Oooh. Lambdas! Or are they Arrow Functions? Neither, precisely, but we'll talk more about them later
How can I use those?
OK, let's talk about lambdas.
They are either an "expression" or a "statement", depending on whether the function type returns a value or is void.
They will be used for defining the initial value of a variable which is a function type.
Looks like they take a statement or a block. Is that a scope?
Unlike Java or Javascript, yes, it is.
The expression is a single statement: "return VALUE";
The block is just like a block elsewhere in ASH.
The other languages allow you to "bind" local variables (if the lambda/arrow function is inside a function) as long as they are "final".
ASH does not have "final" variables.
And although ASH is perfectly happy to let you declare functions inside other functions (or any scope) (and I do that all the time, to clarify the function code and reduce global namespace bloat), we really cannot let lambdas have access to functions and vars in an inner scope.
Therefore, ASH lambdas will be in the global scope - and will have access to all global functions and variables that are lexically visible to them, just like other global functions.
Your turn.
I've put a fair amount of thought into it and see how I would find it useful - and how I could implement it - but now it's your turn to ponder it and offer suggestions and criticisms.
- What is missing that YOU would find useful?
- What is unclear or unspecified?
- What is wrong?
Please keep in mind that ASH is a strongly typed language and that function parameters and return values have types.
Please keep in mind that although I drew on both Java and Javascript for inspiration, this proposal is neither.
(Both of those languages have features that don't make sense in ASH - and ASH can allow things that neither allows.)
Please restrict suggestions to this specific aspect of ASH - the ability to use function objects in variables and parameters and invoke them with arguments.
I am aware, for example, that having GENERIC aggregates would make this even more useful.
It would be nice if ASH had a "list" type - and array (int[]) is sort of like one - but that is built on a Java array, rather than, say, an ArrayList, so you can't add or remove elements from it. (We could fix that by changing the internal implementation to ArrayList, which would not affect any existing programs, but which would add capabilities.)
Even so, I think this proposal would allow you to write things like:
MAP filter(MAP, PREDICATE)
MAP map(MAP, FUNCTION)
VALUE reduce(MAP, FUNCTION)
whereby you could accept a map variable, process its values, and produce another map or a reduced value.
Let's get going.

----
Here are a couple of functions:
Code:
int plus(int x, int y) {
return x + y;
}
int times(int x, int y) {
return x * y;
}
What type are they?
function int (int, int)
typedef function int (int, int) binop;
What type is this?
Code:
void biconsumer(int x, int y)
{
// Do something with the inputs that has a side effect
}
What type is that?
function void (int, int)
typedef function void (int, int) biconsumer;
I use typedefs in every ASH program I write - for aggregates, especially. I'm not sure I've seen them in anybody else's scripts, but I think they are incredibly useful - and equally so with function types. For clarity, I'm going to use the typedefs in further examples, but you don't HAVE to do that.
How can I declare some variables?
Code:
binop op1 = plus;
binop op2 = op1;
binop op3 = (x, y) -> x * 2 + y * 2;
binop op4 = (x, y) -> { return (x ** 2) + (2 * x * y) + y;};
(Oooh. Lambdas! Or are they Arrow Functions? Neither, precisely, but we'll talk more about them later
How can I use those?
Code:
print(op1(5, 6));
// 11
print(op3(2, 3));
// 13
int apply(binop op, int x, int y)
{
return op(x, y);
}
print(apply((x, y) -> x * 2 - y * 2), 10, 5);
// 10
binop[string] ops = {
"+": (x, y) -> x + y,
"-": (x, y) -> x - y,
"*": (x, y) -> x * y,
"/": (x, y) -> x / y
};
int eval(string op, int x, int y)
{
return apply(ops[op], x, y);
}
print(eval("*", eval("+", 2, 3), eval("+", 4, 5)));
// 45
OK, let's talk about lambdas.
They are either an "expression" or a "statement", depending on whether the function type returns a value or is void.
They will be used for defining the initial value of a variable which is a function type.
Code:
binop bo1;
binop bo2 = (x, y) -> INT_INIT_VALUE;
binop bo3 = (x, y) -> { return INT_INIT_VALUE };
biconsumer bc1;
biconsumer bc2 = (x, y) -> {};
Looks like they take a statement or a block. Is that a scope?
Unlike Java or Javascript, yes, it is.
The expression is a single statement: "return VALUE";
The block is just like a block elsewhere in ASH.
The other languages allow you to "bind" local variables (if the lambda/arrow function is inside a function) as long as they are "final".
ASH does not have "final" variables.
And although ASH is perfectly happy to let you declare functions inside other functions (or any scope) (and I do that all the time, to clarify the function code and reduce global namespace bloat), we really cannot let lambdas have access to functions and vars in an inner scope.
Therefore, ASH lambdas will be in the global scope - and will have access to all global functions and variables that are lexically visible to them, just like other global functions.
Your turn.