Skip to content
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

Added `∑`/`sum` support #126

Merged
merged 1 commit into from May 7, 2016

Conversation

@bvssvni
Copy link
Member

commented May 7, 2016

See #119

This adds a loop (both and sum can be used) which initializes a number to 0 and sums up the result from the body, over an index.

Example:

fn main() {
    list := [1, 2, 3, 4]
    sum := ∑ i len(list) { list[i] }
    println(sum)
}

This is logically equal to:

fn main() {
    list := [1, 2, 3, 4]
    sum := 0
    for i len(list) {
        sum += list[i]
    }
    println(sum)
}

Just like a for loop, you can use continue and break. When using continue, it jumps right to the next index without adding the numbers.

I was thinking about adding just the loop and play with it for a while, before deciding whether to add more variants.

Motivation

Dyon has a very simple object model, so there are no iterators like in Rust. It is also an open question whether closures can be made completely safe without a statically type system. To make up for this, we could explore some other syntax.

The benefits of such loops are:

  • The same scope and flexibility as normal loops, including labeled breaks
  • Easy to type and read
  • Can be used within expressions
  • Easy to unroll if we need to make some optimizations
  • Less overhead

Performance

Before:

test tests::bench_add    ... bench:  43,944,256 ns/iter (+/- 6,590,214)
test tests::bench_add_n  ... bench:  15,474,915 ns/iter (+/- 2,362,089)
test tests::bench_array  ... bench:  18,878,560 ns/iter (+/- 3,925,780)
test tests::bench_call   ... bench:  17,556,834 ns/iter (+/- 3,003,651)
test tests::bench_len    ... bench:   3,079,256 ns/iter (+/- 123,155)
test tests::bench_main   ... bench:   1,891,863 ns/iter (+/- 115,946)
test tests::bench_n_body ... bench: 211,619,565 ns/iter (+/- 21,806,030)
test tests::bench_object ... bench:  27,621,531 ns/iter (+/- 5,511,914)

After:

test tests::bench_add    ... bench:  43,954,652 ns/iter (+/- 6,072,920) <--- traditional
test tests::bench_add_n  ... bench:  16,258,668 ns/iter (+/- 2,746,772) <--- short loop
test tests::bench_array  ... bench:  18,041,188 ns/iter (+/- 3,311,291)
test tests::bench_call   ... bench:  17,465,447 ns/iter (+/- 3,015,384)
test tests::bench_len    ... bench:   3,175,244 ns/iter (+/- 124,390)
test tests::bench_main   ... bench:   2,099,142 ns/iter (+/- 126,914)
test tests::bench_n_body ... bench: 209,804,627 ns/iter (+/- 11,616,750)
test tests::bench_object ... bench:  27,480,612 ns/iter (+/- 5,018,216)
test tests::bench_sum    ... bench:   6,705,870 ns/iter (+/- 395,702) <--- ∑ loop

It is 3x faster than the short For loop, and 9x faster than the traditional For loop.

(bench_add_n - bench_main)/(bench_sum - bench_main)
= (16258668-2099142)/(6705870-2099142)
= 3.073662260936613

(bench_add - bench_main)/(bench_sum - bench_main)
= (43954652-2099142)/(6705870-2099142)
= 9.08573503797055

Under the hood

The AST uses ForN to describe both the short For loop and the new ∑ loop. The meta syntax is almost identical, and might be simplified further. The only difference is that it stores it in a different Expression::Sum(Box<ForN>) variant, instead of Expression::ForN(Box<ForN>).

Other ideas

An idea I had was to support both i := 0; i < 10; i += 1 pattern and short version for all loops.

However, I there are several down sides to support the traditional syntax for such loop:

  • The traditional version will be very little used
  • It is easier to make mistakes
  • It requires a separate Expression variant to avoid overhead

Another idea was for optimization. If you write a ∑ loop with a constant end and this is a small number, it is very easy to replace the AST node with an unrolled loop that performs the addition straight forward. However, it needs to check that continue or break is not used within the body, and that the counter is not mutated.

@bvssvni bvssvni merged commit 8fa41e3 into PistonDevelopers:master May 7, 2016

@bvssvni bvssvni deleted the bvssvni:sum branch May 7, 2016

@TheNeikos

This comment has been minimized.

Copy link

commented May 8, 2016

I gotta say I love this concept 💃 (of using ∑ too)

@dobkeratops

This comment has been minimized.

Copy link

commented May 17, 2016

No idea how this language engine works, but do you think you could go as far as inferring the range from the context in the expression. e.g. "sum(i) {foo[i]*bar[i]} " would infer the range of 'i' from its use indexing foo, bar. (whatever syntax makes sense).
This would take it beyond whats possible with high-order functions and macros in other languages; and you could still let the user over-ride some specific range sum(i=0..n-1), whatever.
This maybe way beyond what you need of course, and way too much effort.

@bvssvni bvssvni referenced this pull request May 17, 2016

Closed

Infer range from variable #181

@bvssvni

This comment has been minimized.

Copy link
Member Author

commented May 17, 2016

@dobkeratops I opened #181

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.