This is a complex topic, and in this post I am not able to discuss everything that needs to be discussed. So I will discuss only part of the story.

First: tuples are important enough. I think they should be built-in in a modern language, but maybe having them as half-built-in will be enough in D. Currently in D we have (deprecated) built-in complex numbers that I use only once in a while, and half-usable library defined tuples that I use all the time.

Second: removing comma operator from D has some advantages unrelated to tuple syntax. Even disallowing bad looking C-like code that uses commas is an improvement by itself (but maybe it's not a big enough improvement...).

Third: replacing the packing syntax tuple(x,y) with (x,y) is nice and maybe even expected in a partially functional language as D, but that's _not_ going to improve D usability a lot. What I am asking for is different: I'd like D tuples to support handy unpacking syntax:

1) In function signatures;
2) In foreach;
3) At assignment points;
4) In switch cases.

Note: in the examples below I have used the tuple(x,y) syntax for simplicity, feel free to replace it with a shorter (x,y) syntax if you want.

---------------------------

1) This tuple unpacking use case is important, and it's not what you argue against in the DIP:

int f(tuple(int x, int y)) { return x + y; }
auto pairs = zip([10, 20], [2, 3]);
map!f(pairs)

This is different from what you are arguing against because this "f" is not accepting two normal ints, it accepts a tuple made of two ints, and names them "x" and "y" on the fly.

---------------------------

2)
auto pairs = zip([10, 20], [2, 3]).array();
foreach (i, tuple(x, y); pairs) {}

---------------------------

3) This is probably the most common use case:

auto foo() { return tuple(10, 20); }
auto tuple(x, y) = foo(); // bad syntax
auto (x, y) = foo(); // better syntax
void main() {
    auto a = tuple(1, 2);
    auto tuple(x1, x2) = a; // bad syntax
    auto (y1, y2) = a; // better syntax
}

---------------------------

4) This looks simple, but allows things like using BigInt in switch cases, implementing a very simple but quite handy pattern-matching, etc:

auto v = tuple(10, 20);
final switch (v) {
case tuple(5, y): { x in scope... } break; // y is not a global
    case tuple(x, y): { ... } break; // this covers all cases
}

---------------------------

There are other usage patterns that are handy, but in my opinion the four I have shown here are the most commonly useful ones. See also Bugzilla issues 6365 and 6367, they shows some other cases and ideas and problems.

---------------------------

Tuple singletons: using (1,) as in Python is acceptable. using (1) is not acceptable in my opinion, too much dangerous. tuple(1) is also acceptable, it's longer, but it's not commonly used, so it's OK.

Empty tuples: the (,) syntax proposed in the DIP is not nice, it seems to have one invisible item, but maybe it's acceptable. tuple() is also acceptable, it's longer, but it's not commonly used, so it's OK.

In the end tuples with 0 and 1 items are sometimes useful, but they are not nearly as useful as supporting well tuples with 2 or more items. Scala language agrees with this.

---------------------------

Tuple slicing: it's not a fundamental operation, but it's nice to try to make it work correctly :-) I think not even Haskell does this well.

---------------------------

Summary:
- I think (1,2) is nicer and better than tuple(1,2), and I'd like to have such syntax, but it's not a large improvement and it's not what I am asking for now (less priority). - Supporting tuples with 0 and 1 items is sometimes handy but I think it's not essential. - On the other hand syntax to unpack/destructure tuples in many situations is important for allowing a proper and handy use of tuples in D.

Bye,
bearophile

Reply via email to