expr icon indicating copy to clipboard operation
expr copied to clipboard

Apply/using builtin to reuse result

Open kamstrup opened this issue 4 years ago • 8 comments

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?

kamstrup avatar Mar 25 '20 13:03 kamstrup

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.

antonmedv avatar Mar 25 '20 16:03 antonmedv

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

kamstrup avatar Mar 25 '20 19:03 kamstrup

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})

antonmedv avatar Mar 25 '20 20:03 antonmedv

Not entirely sure what problem is in the above example?

kamstrup avatar Mar 26 '20 12:03 kamstrup

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],
			},
		})
	}

}

antonmedv avatar Mar 26 '20 13:03 antonmedv

Maybe Expr should provide an API for defining own builtins (with type checking and bytecode generations).

antonmedv avatar Mar 26 '20 13:03 antonmedv

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.

kamstrup avatar Mar 28 '20 14:03 kamstrup

Note, what it’s currently not working as builtins are not extendible.

antonmedv avatar Mar 28 '20 14:03 antonmedv

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

antonmedv avatar Feb 04 '23 11:02 antonmedv

Amazing stuff @antonmedv ! ♥️

kamstrup avatar Aug 24 '23 16:08 kamstrup