Implementation request: "(<" and ">)"

I need a champion.

I wonder if someone that works on tweaking the compiler, at least the lexer part, can check if

(<

and

>)

are potentially useable as brackets for tuples. This is for both tuple type declarations and tuple expressions. Are there any parsing problems? (Could they directly abut. Could they be surrounded by, or surround, a parentheses group.) Would "<" have to be banned as a prefix operator, and ">" as a postfix operator?

Extra: would adding "(<<" and ">>)" in addition to the previous tuple brackets be feasible?

Design-wise, I can’t imagine declaring that all existing tuple literals/types have to be rewritten to a new syntax at this point in the language’s evolution. Introducing a new redundant syntax... well, it would have to meet a pretty high bar in terms of solving real problems.

Implementation-wise, though, you could make this specific syntax work by just adding an arbitrary rule to prefer that lexing over any of the alternatives. That is, when the lexer saw a paren, you’d check whether the next character was a < and if so emit a different token.

Like a serious breakage in the current system? Well, I've read here plenty of times that parentheses are so overloaded that single-member tuples are problematic. So going to a more distinct context is a fix. There would be a depreciation period, of course.

But, if you saw one of my other recent threads, the syntax is primarily for sum-tuples, and I realized that it can be reused as an improvement to product (i.e. current) tuples too. The two tuple types would be differentiated by comma vs. semicolon separators, with an extraneous semicolon required for 0- and 1-element sum-tuples.

What about ">)" on the other end? And would we need to ban prefix "<" and postfix ">" operators?

We’re not going to deprecate tuple syntax so that people can feel better about single-element tuples being expressible.

5 Likes

Forgive me but I'm not sure how to interpret that (English is not my first language). It looks to me like it's equivalent to:

We're not going to deprecate tuple syntax just to make it easier for people to accept the fact that single-element tuples are expressible.

But I guess this is not what you meant?

That's how it reads to me, a native English speaker. Why wouldn't he have meant it?

Because single-element tuples are not supposed to exist / be expressible in Swift.

Which (allowing 0, 2, or > 2 members, but not 1) could break variadic generics. Especially if sum-tuples would end up supporting 0 or more members.

Hmm...

What if for a 1-element sum-tuple, we allow direct addressing of the ".0" (or ".whatever") member without a wrapping switch, so it can sometimes act like a 1-element product-tuple.

And/or allow a 1-element sum-tuple type expression act as a 1-element product tuple type expression in a cast:

6  // Int
(6)  // Int with a pair of redundant parentheses
((6))  // Int with two pairs of redundant parentheses
(< 6 >)  // (Int) as a 1-element product tuple
((< 6 >))  // (Int) as a 1-element product tuple with one pair of redundant parentheses
(<(< 6 >)>)  // 1-element product tuple of a 1-element product tuple of Int

Probably need some more thought on this....

Yes, I meant to say “inexpressible,” sorry.

Variadic generics do not require 1-tuples or 1-sums to be distinct from the single element type to be expressible. In Swift's model, an unlabeled 1-tuple is equivalent to the non-tuple element type. It should always be clear from context whether a Swift call is substituting a single type for a variadic or single type variable, so a scalar type can remain scalar in its nongeneric unsubstituted context while appearing as a variadic type in a variadically generic context:

func product<T...>(x: (T...))
func scalar<T>(x: T)

product(x: 0) // T... = (Int)
product(x: (0, "1")) // T... = (Int, String)
scalar(x: 0) // T = Int
2 Likes

Doesn't that mean that one can't distinguish between passing a single 2-tuple, versus passing two arguments? That is, between (in hypothetical ...-is-a-Collection syntax):

  • T.count == 1 & T[0] = (Int, String), and
  • T.count == 2, T[0] = Int & T[1] == String.

If you passed a single 2-tuple, then T... would be bound to one type, the tuple. If you passed two arguments, then it would be bound to two types.

1 Like