expr
expr copied to clipboard
Apply/using builtin to reuse result
Hi - thanks for an awesome library <3
This is a feature request I guess :-) Potentially I could cook it up myself if deemed a sensible feature.
Example: I have added a parseJSON() function to my setup, and now I want to check if the resulting map has 2 keys with certain properties. In pseudocode geo = parseJSON(s); geo.lat > 0 && geo.long < 0
.
Since we can't declare variables like that I was thinking something like an apply
builtin that invokes a closure and returns the result. Eg: `apply(parseJSON(s), {.lat > 0 && .long < 0}).
Or maybe something like this is already possible using some other mechanism?
Hi,
Yes, currently there are no variable assignments in the language. Your builtin idea is good. Currently, you can use something like this:
one([parseJSON(s)], {.lat > 0 && .long < 0})
Maybe some new builtin is needed. Like with
. Also, I'm not sure, but maybe let foo = ...
should be allowed too.
I actually kinda like fact that the language doesn't have local variables. Thanks for the tip with one().
The thing that I can see a need for is "use the result of something in another expression". It could simply be with(parseJOSN(s), { do stuff })
which would probably be ok for most cases. The one place where with()
feels a bit clunky is if you want to chain multiple expressions together. Ie. with(with(parseJOSN(s), { do first thing}), {do second thing})
doesn't read very well :-)
I don't know if a binary operator into
where the right hand side must be a closure would make sense:
parseJSON(s) into { do first thing } into { do second thing }
That would be much easier to read and write at least... It's kinda like piping in a unix shell
I was thinking about adding a pipeline operator as well. But I don't know if it's a good idea as this concept is not then easy.
Tweets |> filter({.Len > 0}) |> map({.Date}) |> sort()
parseJSON(s) |> with({.lat > 0 && .long < 0})
Not entirely sure what problem is in the above example?
It's easy to understand for developers, but hard for managers.
BTW, with
helper can be implemented in expr in it will be allowed to change builtins list:
package main
import (
"fmt"
"github.com/antonmedv/expr"
"github.com/antonmedv/expr/ast"
)
func main() {
env := map[string]interface{}{
"fetch": func() map[string]interface{} {
return map[string]interface{}{
"foo": 1,
"bar": 2,
}
},
}
code := `with(fetch(), {.foo > 0 && .bar == 2})`
program, err := expr.Compile(code, expr.Env(env), expr.Patch(&patcher{}))
if err != nil {
panic(err)
}
output, err := expr.Run(program, env)
if err != nil {
panic(err)
}
fmt.Print(output)
}
type patcher struct{}
func (p *patcher) Enter(_ *ast.Node) {}
func (p *patcher) Exit(node *ast.Node) {
n, ok := (*node).(*ast.FunctionNode)
if ok && n.Name == "with" {
ast.Patch(node, &ast.BuiltinNode{
Name: "one",
Arguments: []ast.Node{
&ast.ArrayNode{
Nodes: []ast.Node{n.Arguments[0]},
},
n.Arguments[1],
},
})
}
}
Maybe Expr should provide an API for defining own builtins (with type checking and bytecode generations).
That would definitely by an option. Thanks for the tip about patching the ast - didn't think of that! Might just go for that option.
Note, what it’s currently not working as builtins are not extendible.
Not it’s possible to provide own builtins via expr.Function https://github.com/antonmedv/expr/blob/master/docs/Getting-Started.md#functions
next: add variables support
Amazing stuff @antonmedv ! ♥️