microsoft / microsoft/qdk

Introduce tuple iteration: for (x, y) in (A, B) {}

Open
#877 3 comments 1 reaction 0 assignees View on GitHub
design needed enhancement language
Dominant language
Rust
Stars
1k
Forks
212
Avg merge
3d 8h
Merged PRs (30d)
65

Description

`Zipped` family of functions and `Enumerated` function create new arrays, which isn't very obvious in a typical usage code and incur performance penalties. If we introduce this pattern, the need for Enumerated and Zipped family will be greatly reduced to the point we might want to remove them.

Here's an example from the old school of writing loops. (Which I belong to :)
```qsharp
for i in 0..Length(xs)-1 {
X(xs[i]);
}
```
You have access to i and you can index an array any way you want. However it has drawbacks.

* You have to know what index range is - i.e. from 0 to |xs|-1. In some languages indexes start with 1.
* You need to be careful with index ranges and index expressions every time your write such loop. In practice most loops are over all the elements of the array.
* You have to write indexing everywhere the 'current' element is used. If there're many of them it gets annoying. You need to think which element of which array is 'current' in every such place. In practice you often start you loop with a local variable holding the 'current' element: let x = xs[i].

A modern way of writing this is
```qsharp
ApplyToEach(X, xs)
```
which I don't like much because it's hard to figure out the complexity by looking at the code. Another way is this.
```qsharp
for q in xs {
X(q);
}
```
It solves the problem of the first approach but hides index from you. You can no longer use index. If you need index, you may write something like this.
```qsharp
for i in IndexRange(xs) {
X(xs[i]);
}
```
It gives you index and hides array bounds. But it doesn't give you the 'current' element. So to get all the benefits you use 'Enumerated'.
```qsharp
for (i,q) in Enumerated(xs) {
R1Frac(1, i, q);
}
```
Now, it's not efficient because it creates an array. Maybe we can solve this by allowing the following construction:
```qsharp
for (i,q) in (IndexRange(xs), xs) {
R1Frac(1, i, q);
}
```
In this case, you have access to the index, 'current' element and index range is hidden.

Complication: What to do if the number of elements in these ranges are different? Use the shortest one? Fail execution?

Contributor guide

Open the contributing guide

Research direction

Start with the proposed Q# loop examples in this issue, especially tuple iteration over IndexRange(xs) and xs. Define the behavior when the iterated ranges have different lengths, then inspect the compiler and language tests to determine where the syntax and semantics belong. Done means the construction is specified, implemented, and covered by tests.

Written by the indexing model from the issue text.

Assessment

Domain
compilers, quantum-computing
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.