jmespath / jmespath/jmespath.jep
[Initial feedback] Potential proposal for user-defined function
- Dominant language
- Python
- Stars
- 10
- Forks
- 3
- PR merge metrics
- No merged PRs in 30d
Description
I’m investigating user-defined _lambda-like_ functions as [discussed here](https://github.com/jmespath/jmespath.jep/pull/18#issuecomment-1483085482).
Is this something of interest to discuss here?
## User-defined functions
JMESPath already supports `expression-type` which are, conceptually, anonymous functions without arguments.
I have toyed with the idea of extending exprefs to support named arguments.
An updated grammar that works looks like this:
```abnf
expression-type = "&" expression / "<" arguments ">" "=>" expression
arguments = variable-ref *( "," variable-ref
```
It requires a [new token](https://github.com/jmespath-community/typescript-jmespath/blob/d1fecf6c1d7aa535a736ff669aa59a6c1135090f/src/Lexer.type.ts#L7) `=>` which is even not absolutely necessary. Parsing involves a [new entry](https://github.com/jmespath-community/typescript-jmespath/blob/d1fecf6c1d7aa535a736ff669aa59a6c1135090f/src/Parser.ts#L180-L210) in the `nud()` function to match on token `<` (less-than-sign).
## Reduce function
As `expression-type` are only supported as function arguments, a `reduce()`
function [could work](https://github.com/jmespath-community/typescript-jmespath/blob/d1fecf6c1d7aa535a736ff669aa59a6c1135090f/src/Runtime.ts#L481-L501) like this:
`` reduce(array $array, any $seed, expr [any, any]->any) ``
Example:
```json
[1, 3, 5, 7]
```
```jmespath
reduce(@, `1`, <$acc, $cur> => $acc × $cur)
```
The `reduce()` function _knows_ how to iterate over its first array argument
and repeatedly create bindings for both function arguments `$acc` and `$cur`.
Funnily enough, the lambda function does not support natively the `@` current node.
An argument can be made that it could be similar to the `$cur` argument. In that case,
a choice must be made as to which "context" is specified when evaluating the expref.
## Reusing user-defined functions
I then investigated what reusing user-defined functions could look like.
This requires a mechanism to store or hold functions, possibly by name,
as well as a way to call those functions with appropriate parameters.
By taking advantage of the hopefully upcoming `let-expression` design, one
could bind the anonymous function to a variable reference. Then, syntax for
calling the function using this variable must be extended and supported in
the grammar.
In the following expression:
```jmespath
let $concat = <$lhs, $rhs> => join('-', [$lhs, $rhs])
in $concat('4', '2')
```
`$concat` is bound to the `` <$lhs, $rhs> => join('-', [$lhs, $rhs]) `` lambda `expression-type`. Then, `$concat('4', '2')` invokes the function indirectly using the `$concat` variable reference.
The following grammar changes is required:
```abnf
function-expression = ( unquoted-string / variable-ref ) no-args / one-or-more-args )
```
Again, [implementation is quite easy](https://github.com/jmespath-community/typescript-jmespath/blob/d1fecf6c1d7aa535a736ff669aa59a6c1135090f/src/TreeInterpreter.ts#L285-L307).
Funnily enough, having also implemented `arithmetic-expression` grammar rules, I was then able to [implement the recursive Fibonacci sequence](https://github.com/jmespath-community/typescript-jmespath/blob/d1fecf6c1d7aa535a736ff669aa59a6c1135090f/test/jmespath-interpreter.spec.ts#L17) using the following expression:
```jmespath
let $fib = <$n> => (
($n == `0` && `0`) ||
(($n == `1` && `1`) ||
( $fib($n - `1`) + $fib($n - `2`) )
)
) in
$fib(@)
```
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.