8
$\begingroup$

While single-item tuples aren't particularly useful, disallowing them entirely, or allowing them but with few to no valid representations in syntax, may also be undesirable. However, since tuples typically use parentheses, the default comma-separated syntax would make a tuple literal indistinguishable (without context) from an expression wrapped in parentheses for grouping purposes. E.g., is (2 + 2) the tuple (4), or just 4?

What are some approaches which could be used to address this ambiguity?

$\endgroup$

3 Answers 3

7
$\begingroup$

Rust-style: Trailing comma

Since Rust allows and even encourages trailing commas in arrays, structs, and so on, it also allows them in tuples, giving you a simple, unambiguous single-item tuple syntax: (2 + 2,). This also works for types: (Option<String>,).

$\endgroup$
3
  • $\begingroup$ Swift decided to allow trailing commas in arrays and dictionaries, but not tuples. Why they did that is beyond me. $\endgroup$
    – Bbrk24
    Jun 23, 2023 at 13:05
  • 5
    $\begingroup$ This is Python style, too. $\endgroup$
    – kaya3
    Jun 23, 2023 at 13:10
  • 1
    $\begingroup$ Note that Python tuples don't require parenthesis. a = 1, will assign a single-item tuple to the name a, and similarly a, = ... is a common way of destructuring a single-item tuple. $\endgroup$
    – BoppreH
    Jun 30, 2023 at 12:07
3
$\begingroup$

Labels

Some languages, such as Swift and C#, let you label the items in tuple types:

var t: (high: Int, low: UInt) = largeInteger.multipliedFullWidth(by: anotherLargeInteger)

These particular languages also have _ for an "ignore me" identifier:

let ignoresSecondArgument: (Int, Int) -> Int = { i, _ in i }
_ = ignoreTheReturnValue()
let yesThisIsLegal: (_: Int, _: Double) = (1, 2.0)
let soIsThis: (Int, Double) = (_: 3, _: 4.0)

So, you could require that single-element tuples have item labels, and then unlabelled single-element tuples use _:

let singleElementTuple = (_: 1)
let labelledSingleElementTuple = (x: 1)

Note: In Swift, labelled single-element tuples are illegal, and unlabelled single-element tuples are identical to the type that they wrap around. This means that, as strange as it looks, this is valid:

print((_: 2 + 3) / 5) // prints "1"
$\endgroup$
1
$\begingroup$

Compiler inference

Another, more complex option would be compiler inference. In a statically and strongly typed language, and one where few operators can be used on a tuple, it may be relatively simple to see where a tuple is expected, and check each possible set of parentheses to see whether or not it would make sense to treat it as a tuple. E.g.:

fn unwrap_me_once(x: (int)) -> int {
    x.0
}

unwrap_me_once((2 + 2)); // This must be a tuple
unwrap_me_once(((2 + 2))); // Either interpretation could be valid, but both are the same
unwrap_me_once(((2 + 2) * 4)); // The inner parentheses can't be a tuple

fn shame_on_me(x: int) -> int {
    x
}

shame_on_me((2 + 2).0); // This must be a tuple, since accessing an item of an int would be invalid
shame_on_me(((1 + 1) + (1 + 1))); // These must all be grouping, since + doesn't work on tuples, and the function expects an int instead of a single-item tuple
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .