Skip to content

Feature reuse result of common expr #393

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from

Conversation

zhuliquan
Copy link
Contributor

I find a tiny improvement for compiler, which use save result of common sub-expr and used it after.
for example:

(veryHeavyFunc() + tinyFunc1()) + (veryHeavyFunc() + tinyFunc2())

we can reuse result of veryHeavyFunc()
I define a new parameter of Config called AllowReuseCommon, if it's set true, we will reused result of common expr which has been evaluated before.
I define three new opcodes for program compiler.

OpSaveCommon // save result of common expr to vm.commonCache 
OpLoadCommon // load result of common expr from cache to vm.stack
OpJumpIfSaveCommon // check whether result of common expr has been saved before, If the common expr has already been evaluated and been saved to , it will be evaluated beyond the current common expr. 

@antonmedv
Copy link
Member

antonmedv commented Jul 25, 2023

Benchmark results not very promising:

goos: linux
goarch: amd64
pkg: github.com/antonmedv/expr
cpu: Intel(R) Xeon(R) Platinum 81M CPU @ 2.60GHz
                                       │ /tmp/old.txt │              /tmp/new.txt               │
                                       │    sec/op    │    sec/op     vs base                   │
_expr-2                                   6.7n ± 1%    661.0n ± 4%  +254.17% (p=0.000 n=10)
_expr_reuseVm-2                           2.66n ± 4%   8.15n ± 4%   +16.72% (p=0.000 n=10)
_filter-2                                 27.30µ ± 4%    25.16µ ± 2%    -7.81% (p=0.000 n=10)
_len-2                                    110.6n ± 3%    567.0n ± 4%  +412.43% (p=0.000 n=10)
_access-2                                 162.2n ± 7%    602.9n ± 3%  +271.56% (p=0.000 n=10)
_accessMap-2                              168.4n ± 2%    625.8n ± 3%  +271.50% (p=0.000 n=10)
_callFunc-2                               956.4n ± 2%   1413.5n ± 5%   +47.79% (p=0.000 n=10)
_callMethod-2                             949.5n ± 2%   1474.5n ± 1%   +55.29% (p=0.000 n=10)
_callField-2                              193.2n ± 3%    690.4n ± 1%  +257.44% (p=0.000 n=10)
_callFast-2                               205.8n ± 3%    705.6n ± 3%  +242.80% (p=0.000 n=10)
_callConstExpr-2                          166.2n ± 2%    646.8n ± 5%  +289.08% (p=0.000 n=10)
_largeStructAccess-2                      369.9n ± 7%    729.3n ± 1%   +97.19% (p=0.000 n=10)
_largeNestedStructAccess-2                373.2n ± 8%    741.6n ± 1%   +98.73% (p=0.000 n=10)
_largeNestedArrayAccess-2                 4.454m ± 3%    4.486m ± 9%         ~ (p=0.353 n=10)
_realWorld-2                              527.0n ± 3%    989.4n ± 4%   +87.77% (p=0.000 n=10)
_realWorld_reuseVm-2                      446.5n ± 3%    435.5n ± 1%    -2.45% (p=0.035 n=10)
_realWorldInsane-2                        215.9µ ± 4%
_realWorldInsane/no_common_reused-2                      198.4µ ± 5%
_realWorldInsane/allow_common_reused-2                   151.2µ ± 6%
geomean                                   925.4n         2.417µ       +115.30%                ¹
¹ benchmark set differs from baseline; geomeans may not be comparable

                                       │  /tmp/old.txt  │                /tmp/new.txt                │
                                       │      B/op      │     B/op      vs base                      │
_expr-2                                    32.00 ± 0%     1536.00 ± 0%  +4700.00% (p=0.000 n=10)
_expr_reuseVm-2                            0.000 ± 0%       0.000 ± 0%          ~ (p=1.000 n=10) ¹
_filter-2                                2.617Ki ± 0%     3.141Ki ± 0%    +20.00% (p=0.000 n=10)
_len-2                                     32.00 ± 0%     1536.00 ± 0%  +4700.00% (p=0.000 n=10)
_access-2                                  32.00 ± 0%     1536.00 ± 0%  +4700.00% (p=0.000 n=10)
_accessMap-2                               32.00 ± 0%     1536.00 ± 0%  +4700.00% (p=0.000 n=10)
_callFunc-2                                192.0 ± 0%      1696.0 ± 0%   +783.33% (p=0.000 n=10)
_callMethod-2                              192.0 ± 0%      1696.0 ± 0%   +783.33% (p=0.000 n=10)
_callField-2                               96.00 ± 0%     1600.00 ± 0%  +1566.67% (p=0.000 n=10)
_callFast-2                                96.00 ± 0%     1600.00 ± 0%  +1566.67% (p=0.000 n=10)
_callConstExpr-2                           96.00 ± 0%     1600.00 ± 0%  +1566.67% (p=0.000 n=10)
_largeStructAccess-2                       62.00 ± 2%     1573.00 ± 0%  +2437.10% (p=0.000 n=10)
_largeNestedStructAccess-2                 63.00 ± 2%     1573.00 ± 0%  +2396.83% (p=0.000 n=10)
_largeNestedArrayAccess-2                10.08Mi ± 0%     10.08Mi ± 0%          ~ (p=0.839 n=10)
_realWorld-2                               416.0 ± 0%      1920.0 ± 0%   +361.54% (p=0.000 n=10)
_realWorld_reuseVm-2                       384.0 ± 0%       384.0 ± 0%          ~ (p=1.000 n=10) ¹
_realWorldInsane-2                       16.32Ki ± 1%
_realWorldInsane/no_common_reused-2                       14.95Ki ± 0%
_realWorldInsane/allow_common_reused-2                    28.67Ki ± 0%
geomean                                               ²                  +875.53%                ³ ²
¹ all samples are equal
² summaries must be >0 to compute geomean
³ benchmark set differs from baseline; geomeans may not be comparable

                                       │ /tmp/old.txt │              /tmp/new.txt               │
                                       │  allocs/op   │ allocs/op   vs base                     │
_expr-2                                  1.000 ± 0%     2.000 ± 0%  +100.00% (p=0.000 n=10)
_expr_reuseVm-2                          0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=10) ¹
_filter-2                                176.0 ± 0%     172.0 ± 0%    -2.27% (p=0.000 n=10)
_len-2                                   1.000 ± 0%     2.000 ± 0%  +100.00% (p=0.000 n=10)
_access-2                                1.000 ± 0%     2.000 ± 0%  +100.00% (p=0.000 n=10)
_accessMap-2                             1.000 ± 0%     2.000 ± 0%  +100.00% (p=0.000 n=10)
_callFunc-2                              6.000 ± 0%     7.000 ± 0%   +.67% (p=0.000 n=10)
_callMethod-2                            6.000 ± 0%     7.000 ± 0%   +16.67% (p=0.000 n=10)
_callField-2                             2.000 ± 0%     3.000 ± 0%   +50.00% (p=0.000 n=10)
_callFast-2                              2.000 ± 0%     3.000 ± 0%   +50.00% (p=0.000 n=10)
_callConstExpr-2                         2.000 ± 0%     3.000 ± 0%   +50.00% (p=0.000 n=10)
_largeStructAccess-2                     4.000 ± 0%     5.000 ± 0%   +.00% (p=0.000 n=10)
_largeNestedStructAccess-2               4.000 ± 0%     5.000 ± 0%   +25.00% (p=0.000 n=10)
_largeNestedArrayAccess-2                2.000 ± 0%     3.000 ± 0%   +50.00% (p=0.000 n=10)
_realWorld-2                             2.000 ± 0%     3.000 ± 0%   +50.00% (p=0.000 n=10)
_realWorld_reuseVm-2                     1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=10) ¹
_realWorldInsane-2                       860.0 ± 0%
_realWorldInsane/no_common_reused-2                     840.0 ± 0%
_realWorldInsane/allow_common_reused-2                  567.0 ± 0%
geomean                                             ²                +41.[30](https://github.com/antonmedv/expr/actions/runs/5658005304/job/15332440349?pr=393#step:8:31)%                ³ ²
¹ all samples are equal
² summaries must be >0 to compute geomean
³ benchmark set differs from baseline; geomeans may not be comparable

https://github.com/antonmedv/expr/actions/runs/5658005304/job/15332440349?pr=393

n.SetSubExpr(strconv.Quote(n.Value))
}

func (c *compiler) analyzeCommonConstantNode(n *ast.ConstantNode) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be better to just implement a toString() method for nodes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emm, you meaning String() func in golang?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

Copy link
Contributor Author

@zhuliquan zhuliquan Jul 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

At the beginning, I also wanted to use the interface of String, but when I used it later, I found that I still needed an interface to set the expression for checking common sub-expr. because of the expression optimzation in package optimizer, I can't set it at parse expr. meanwhile, I found that there are function to set/get file.Location in ast.Node. so I added GetSubExpr/SetSubExpr similarly.

@@ -21,6 +21,8 @@ type Config struct {
ConstFns map[string]reflect.Value
Visitors []ast.Visitor
Functions map[string]*builtin.Function

AllowReuseCommon bool // allow cache common sub-expr computed result, aimed to reuse already computed result
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For functions will make more sense a config like this:

expr.PureFunc("fib")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emm, you mean to add Option function like expr.Function?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to expr.ConstExpr

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to expr.ConstExpr
Yes, I already have added this function expr.AllowReuseCommon() in expr.go.
https://github.com/zhuliquan/expr/blob/f84c5a98f555a187a861bcc94218d80d0fa2c7ed/expr.go#L127

@@ -21,6 +22,14 @@ func root(l *lexer) stateFn {
l.error("%v", err)
}
l.emitValue(String, str)
case r == '`': // raw string case
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to split into two PRs.

Copy link
Contributor Author

@zhuliquan zhuliquan Jul 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, I can't to split into two prs, I have some projects in my company also uses raw_string and resue common two features in my fork branch. whether I propose another PR does not contain the raw string feature, or you merge the previous PR first and then deal with this PR

if vm.stack == nil {
vm.stack = make([]interface{}, 0, 2)
vm.stack = make([]interface{}, 0, 64)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why to increate default stack size?

Copy link
Contributor Author

@zhuliquan zhuliquan Jul 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why to increate default stack size?

which make it bigger can reduce times of extend capacity of stack in running very long expression (i.g. very long array expr), and I like number of 64, if you don't like it, I will recover to default 2

vm/vm.go Outdated

if vm.scopes != nil {
if vm.scopes == nil {
vm.scopes = make([]*Scope, 0, 64)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not every expression uses scopes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you

vm/vm.go Outdated

case OpNot:
v := vm.pop().(bool)
vm.push(!v)
vm.push(!(vm.pop().(bool)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional variable makes code more readable.

@antonmedv
Copy link
Member

This optimization is actually very interesting to do, but I think it should be done on the compile spec with AST transformation.

But the first step will be to add the variables support in the language.

So for example this expression:

foo[0].bar.baz == 2 && foo[0].bar.goz == 3

Can be changed into:

let tmp = foo[0].bar; tmp.baz == 2 && tmp.goz == 3

fix: extract commonCache to temp variable in vm.go
@zhuliquan zhuliquan closed this Jul 26, 2023
@zhuliquan zhuliquan reopened this Jul 26, 2023
@zhuliquan
Copy link
Contributor Author

Benchmark results not very promising:

goos: linux
goarch: amd64
pkg: github.com/antonmedv/expr
cpu: Intel(R) Xeon(R) Platinum 81M CPU @ 2.60GHz
                                       │ /tmp/old.txt │              /tmp/new.txt               │
                                       │    sec/op    │    sec/op     vs base                   │
_expr-2                                   6.7n ± 1%    661.0n ± 4%  +254.17% (p=0.000 n=10)
_expr_reuseVm-2                           2.66n ± 4%   8.15n ± 4%   +16.72% (p=0.000 n=10)
_filter-2                                 27.30µ ± 4%    25.16µ ± 2%    -7.81% (p=0.000 n=10)
_len-2                                    110.6n ± 3%    567.0n ± 4%  +412.43% (p=0.000 n=10)
_access-2                                 162.2n ± 7%    602.9n ± 3%  +271.56% (p=0.000 n=10)
_accessMap-2                              168.4n ± 2%    625.8n ± 3%  +271.50% (p=0.000 n=10)
_callFunc-2                               956.4n ± 2%   1413.5n ± 5%   +47.79% (p=0.000 n=10)
_callMethod-2                             949.5n ± 2%   1474.5n ± 1%   +55.29% (p=0.000 n=10)
_callField-2                              193.2n ± 3%    690.4n ± 1%  +257.44% (p=0.000 n=10)
_callFast-2                               205.8n ± 3%    705.6n ± 3%  +242.80% (p=0.000 n=10)
_callConstExpr-2                          166.2n ± 2%    646.8n ± 5%  +289.08% (p=0.000 n=10)
_largeStructAccess-2                      369.9n ± 7%    729.3n ± 1%   +97.19% (p=0.000 n=10)
_largeNestedStructAccess-2                373.2n ± 8%    741.6n ± 1%   +98.73% (p=0.000 n=10)
_largeNestedArrayAccess-2                 4.454m ± 3%    4.486m ± 9%         ~ (p=0.353 n=10)
_realWorld-2                              527.0n ± 3%    989.4n ± 4%   +87.77% (p=0.000 n=10)
_realWorld_reuseVm-2                      446.5n ± 3%    435.5n ± 1%    -2.45% (p=0.035 n=10)
_realWorldInsane-2                        215.9µ ± 4%
_realWorldInsane/no_common_reused-2                      198.4µ ± 5%
_realWorldInsane/allow_common_reused-2                   151.2µ ± 6%
geomean                                   925.4n         2.417µ       +115.30%                ¹
¹ benchmark set differs from baseline; geomeans may not be comparable

                                       │  /tmp/old.txt  │                /tmp/new.txt                │
                                       │      B/op      │     B/op      vs base                      │
_expr-2                                    32.00 ± 0%     1536.00 ± 0%  +4700.00% (p=0.000 n=10)
_expr_reuseVm-2                            0.000 ± 0%       0.000 ± 0%          ~ (p=1.000 n=10) ¹
_filter-2                                2.617Ki ± 0%     3.141Ki ± 0%    +20.00% (p=0.000 n=10)
_len-2                                     32.00 ± 0%     1536.00 ± 0%  +4700.00% (p=0.000 n=10)
_access-2                                  32.00 ± 0%     1536.00 ± 0%  +4700.00% (p=0.000 n=10)
_accessMap-2                               32.00 ± 0%     1536.00 ± 0%  +4700.00% (p=0.000 n=10)
_callFunc-2                                192.0 ± 0%      1696.0 ± 0%   +783.33% (p=0.000 n=10)
_callMethod-2                              192.0 ± 0%      1696.0 ± 0%   +783.33% (p=0.000 n=10)
_callField-2                               96.00 ± 0%     1600.00 ± 0%  +1566.67% (p=0.000 n=10)
_callFast-2                                96.00 ± 0%     1600.00 ± 0%  +1566.67% (p=0.000 n=10)
_callConstExpr-2                           96.00 ± 0%     1600.00 ± 0%  +1566.67% (p=0.000 n=10)
_largeStructAccess-2                       62.00 ± 2%     1573.00 ± 0%  +2437.10% (p=0.000 n=10)
_largeNestedStructAccess-2                 63.00 ± 2%     1573.00 ± 0%  +2396.83% (p=0.000 n=10)
_largeNestedArrayAccess-2                10.08Mi ± 0%     10.08Mi ± 0%          ~ (p=0.839 n=10)
_realWorld-2                               416.0 ± 0%      1920.0 ± 0%   +361.54% (p=0.000 n=10)
_realWorld_reuseVm-2                       384.0 ± 0%       384.0 ± 0%          ~ (p=1.000 n=10) ¹
_realWorldInsane-2                       16.32Ki ± 1%
_realWorldInsane/no_common_reused-2                       14.95Ki ± 0%
_realWorldInsane/allow_common_reused-2                    28.67Ki ± 0%
geomean                                               ²                  +875.53%                ³ ²
¹ all samples are equal
² summaries must be >0 to compute geomean
³ benchmark set differs from baseline; geomeans may not be comparable

                                       │ /tmp/old.txt │              /tmp/new.txt               │
                                       │  allocs/op   │ allocs/op   vs base                     │
_expr-2                                  1.000 ± 0%     2.000 ± 0%  +100.00% (p=0.000 n=10)
_expr_reuseVm-2                          0.000 ± 0%     0.000 ± 0%         ~ (p=1.000 n=10) ¹
_filter-2                                176.0 ± 0%     172.0 ± 0%    -2.27% (p=0.000 n=10)
_len-2                                   1.000 ± 0%     2.000 ± 0%  +100.00% (p=0.000 n=10)
_access-2                                1.000 ± 0%     2.000 ± 0%  +100.00% (p=0.000 n=10)
_accessMap-2                             1.000 ± 0%     2.000 ± 0%  +100.00% (p=0.000 n=10)
_callFunc-2                              6.000 ± 0%     7.000 ± 0%   +.67% (p=0.000 n=10)
_callMethod-2                            6.000 ± 0%     7.000 ± 0%   +16.67% (p=0.000 n=10)
_callField-2                             2.000 ± 0%     3.000 ± 0%   +50.00% (p=0.000 n=10)
_callFast-2                              2.000 ± 0%     3.000 ± 0%   +50.00% (p=0.000 n=10)
_callConstExpr-2                         2.000 ± 0%     3.000 ± 0%   +50.00% (p=0.000 n=10)
_largeStructAccess-2                     4.000 ± 0%     5.000 ± 0%   +.00% (p=0.000 n=10)
_largeNestedStructAccess-2               4.000 ± 0%     5.000 ± 0%   +25.00% (p=0.000 n=10)
_largeNestedArrayAccess-2                2.000 ± 0%     3.000 ± 0%   +50.00% (p=0.000 n=10)
_realWorld-2                             2.000 ± 0%     3.000 ± 0%   +50.00% (p=0.000 n=10)
_realWorld_reuseVm-2                     1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=10) ¹
_realWorldInsane-2                       860.0 ± 0%
_realWorldInsane/no_common_reused-2                     840.0 ± 0%
_realWorldInsane/allow_common_reused-2                  567.0 ± 0%
geomean                                             ²                +41.[30](https://github.com/antonmedv/expr/actions/runs/5658005304/job/15332440349?pr=393#step:8:31)%                ³ ²
¹ all samples are equal
² summaries must be >0 to compute geomean
³ benchmark set differs from baseline; geomeans may not be comparable

https://github.com/antonmedv/expr/actions/runs/5658005304/job/15332440349?pr=393

it have been improvement about 25% according to benchmark report( case allow_common_reused is set AllowReuseCommon as true). and this fact is meeting to our company project.

Benchmark_realWorldInsane/no_common_reused-2         	    5779	    211073 ns/op	   15312 B/op	     840 allocs/op
Benchmark_realWorldInsane/no_common_reused-2         	    5876	    207645 ns/op	   15312 B/op	     840 allocs/op
Benchmark_realWorldInsane/no_common_reused-2         	    5649	    198302 ns/op	   15312 B/op	     840 allocs/op
Benchmark_realWorldInsane/no_common_reused-2         	    6574	    200611 ns/op	   15312 B/op	     840 allocs/op
Benchmark_realWorldInsane/no_common_reused-2         	    5628	    195742 ns/op	   15312 B/op	     840 allocs/op
Benchmark_realWorldInsane/no_common_reused-2         	    6693	    198448 ns/op	   15312 B/op	     840 allocs/op
Benchmark_realWorldInsane/no_common_reused-2         	    6628	    195521 ns/op	   15312 B/op	     840 allocs/op
Benchmark_realWorldInsane/no_common_reused-2         	    6267	    193898 ns/op	   15312 B/op	     840 allocs/op
Benchmark_realWorldInsane/no_common_reused-2         	    6444	    191018 ns/op	   15312 B/op	     840 allocs/op
Benchmark_realWorldInsane/no_common_reused-2         	    5686	    203448 ns/op	   15312 B/op	     840 allocs/op
Benchmark_realWorldInsane/allow_common_reused-2      	    7782	    150003 ns/op	   29360 B/op	     567 allocs/op
Benchmark_realWorldInsane/allow_common_reused-2      	    7873	    161597 ns/op	   29360 B/op	     567 allocs/op
Benchmark_realWorldInsane/allow_common_reused-2      	    7315	    155657 ns/op	   29360 B/op	     567 allocs/op
Benchmark_realWorldInsane/allow_common_reused-2      	    7515	    159524 ns/op	   29360 B/op	     567 allocs/op
Benchmark_realWorldInsane/allow_common_reused-2      	    7596	    155227 ns/op	   29360 B/op	     567 allocs/op
Benchmark_realWorldInsane/allow_common_reused-2      	    7236	    150831 ns/op	   29360 B/op	     567 allocs/op
Benchmark_realWorldInsane/allow_common_reused-2      	    7[179](https://github.com/antonmedv/expr/actions/runs/5658005304/job/15332440349?pr=393#step:5:180)	    150344 ns/op	   29360 B/op	     567 allocs/op
Benchmark_realWorldInsane/allow_common_reused-2      	    7844	    151549 ns/op	   29360 B/op	     567 allocs/op
Benchmark_realWorldInsane/allow_common_reused-2      	    8[182](https://github.com/antonmedv/expr/actions/runs/5658005304/job/15332440349?pr=393#step:5:183)	    147485 ns/op	   29360 B/op	     567 allocs/op
Benchmark_realWorldInsane/allow_common_reused-2      	    7894	    147235 ns/op	   29360 B/op	     567 allocs/op

@antonmedv
Copy link
Member

Setting AllowReuseCommon on or off is not meaningful. As both of them perform much worse for all expressions.

This PR, in its current form, significantly slows downs expression executions.

@zhuliquan
Copy link
Contributor Author

zhuliquan commented Jul 27, 2023

Setting AllowReuseCommon on or off is not meaningful. As both of them perform much worse for all expressions.

This PR, in its current form, significantly slows downs expression executions.

I don't agree it, the fact that allow_common_reused have beat with no_common_reuse proves it work on expression with may common sub-expressions. In fact, this feature only has a huge performance improvement for expression with many common sub-expressions. In general case, it doesn't help for running fast, and it also need a extra memory for temp variable commonCache and more compile time. I'm also worried about this, so I set default value of AllowReuseCommon as false, and using expr.AllowReuseCommon(true) to enable this feature. Meanwhile, I just add common reuse features on BinaryNode/CallNode in compiler.go.

@zhuliquan
Copy link
Contributor Author

This optimization is actually very interesting to do, but I think it should be done on the compile spec with AST transformation.

But the first step will be to add the variables support in the language.

So for example this expression:

foo[0].bar.baz == 2 && foo[0].bar.goz == 3

Can be changed into:

let tmp = foo[0].bar; tmp.baz == 2 && tmp.goz == 3

I also agree this plan. but it means that expr must support variable assignment and variable declarations,variable type inference, whether it make expr more complex.

@antonmedv
Copy link
Member

Variable in expr already in plan #101, and I'm actually want to implement it soon.

@zhuliquan
Copy link
Contributor Author

Variable in expr already in plan #101, and I'm actually want to implement it soon.

nice

@zhuliquan zhuliquan closed this Aug 14, 2023
@zhuliquan zhuliquan deleted the feature-reuse_common branch August 14, 2023 14:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants