Stream: ideas

Topic: Change tuple syntax from () to {}?


view this post on Zulip Sky Rose (Dec 17 2024 at 14:11):

What if we used curly braces for tuples instead of parens?

old: ("abc", 123)
new: {"abc", 123}

Pros:

Cons:

There would be a couple new ambiguities with records, but these are minor compared to ambiguity with parens for grouping:

view this post on Zulip Oskar Hahn (Dec 17 2024 at 14:39):

I also thought about this. But I think this is not possible.

Here is an example:

hello = "hello"
world = "world
value = {hello, world}

With the current syntax, this is a record of type {hello: Str, world: Str}. When a tuple also uses {}, then it also could be of type {Str, Str}

view this post on Zulip Anthony Bullard (Dec 17 2024 at 14:46):

Yeah, you can't do this and ALSO have field punning in records. And the advantages of punning outweight any sort of benefit one could imagine from changing the tuple syntax. And the only other brace pair that is unused in the language is <> and I don't think anyone wants to use <"abc", 123>. The other option is to do what Elixir does and use a sigil for differentiating between records and tuples:

Tuple: {"abc", 123}
Struct: %{hello, world} or #{hello, world}

I think the sigil approach makes tuples more ergonomic than records, which is not what I think we want to do

view this post on Zulip Anthony Bullard (Dec 17 2024 at 14:47):

I guess you could flip the game, and make a tuple have the sigil, but I've never seen that.

view this post on Zulip Anthony Bullard (Dec 17 2024 at 14:49):

And I wonder if there really is ambiguity with parens for grouping, since we don't allow for 1-tuples (which IMO don't make a lot of sense)

view this post on Zulip Anthony Bullard (Dec 17 2024 at 14:50):

You see a paren, you parse a list of expressions ending with a paren. If you have a single expression - it's just a group, more than one - it's a tuple.

view this post on Zulip Anthony Bullard (Dec 17 2024 at 14:52):

I think the only real advantage of getting tuples off of the () syntax is getting a more familiar lambda syntax for when we move to parens-and-commas style application

view this post on Zulip Sky Rose (Dec 17 2024 at 16:07):

There really is ambiguity with grouping, as shown in the thread I linked. They managed to figure it out in that case, but even if the parser has a rule to follow, it can still be surprising for the programmer.

Joshua Warner said:

TBH tuples are very weird at a syntactic level. As are exts.
This grammar is balancing on a knife's edge ;)

view this post on Zulip Sky Rose (Dec 17 2024 at 16:09):

Good point about the parens function calling syntax, that's a 3rd use of parens. I think it's less likely to cause ambiguity but it does still add confusion.

view this post on Zulip Sky Rose (Dec 17 2024 at 16:13):

I forgot about pinning though, you're right that means we need slightly different tuple and record syntaxes.

The sigil approach seems reasonable but adds additional weirdness. It still suggests that records and tuples are related, which is good.

Though a sigil could also resolve the ambiguously even with parens. Like if tuples were #("abc", 123). (Edit: Gleam has this tuple syntax)

view this post on Zulip Sky Rose (Dec 17 2024 at 16:16):

Someone in the other thread also mentioned using a different delimiter (instead of comma) for tuples, like ("abc"; 123) or {"abc"; 123}, which would work for disambiguating from grouping with parens or pinned records with braces, but I think it'd be more confusing than a new bracket.

Edit: Rejected

view this post on Zulip Richard Feldman (Dec 17 2024 at 16:35):

destructuring in patterns is the bigger deal here:

{ x, y } ->

I understand the appeal of the idea, but personally, I look at that one line of code and instantly conclude that curly braces cannot be overloaded for both records and tuples :big_smile:

view this post on Zulip Richard Feldman (Dec 17 2024 at 16:37):

also I think using semicolons for collection delimiters would be a huge mistake. That sort of aesthetic choice is like beginner repellent. :sweat_smile:

view this post on Zulip Richard Feldman (Dec 17 2024 at 16:38):

it might have been reasonable in the 1970s but today the most common reaction is "ew, what is that"

view this post on Zulip Sky Rose (Dec 17 2024 at 16:45):

I just caught up with the other thread which also discussed different sigils and brackets for tuples https://roc.zulipchat.com/#narrow/stream/304641-ideas/topic/Remove.20the.20need.20for.20backslashes.20for.20lambdas.3F/ I thought this was an original idea but I guess not!

I do think there's a lot of reasons for changing the tuple syntax that have popped up recently, but there's no clear frontrunner for what to change to.

view this post on Zulip jan kili (Dec 17 2024 at 17:01):

Random idea

{0 x, y } ->

view this post on Zulip jan kili (Dec 17 2024 at 17:08):

However, I'm still unconvinced that field punning can't be preserved even if they both use { } - that just seems like a distinction that the compiler can handle, writers will understand, and readers won't notice.

view this post on Zulip Sam Mohr (Dec 17 2024 at 17:09):

A prefix sigil seems like the best option still, but I'm personally not convinced that using a different syntax is a positive change for tuples in the first place.

view this post on Zulip jan kili (Dec 17 2024 at 17:09):

Does anyone have a full app example in mind that would cover 80% of { } use cases to discuss implementation options?

view this post on Zulip jan kili (Dec 17 2024 at 17:11):

The compiler infers varied types for numbers based on where they come from and how they're used, and this feel similar.

view this post on Zulip Sam Mohr (Dec 17 2024 at 17:15):

Why zero?

view this post on Zulip jan kili (Dec 17 2024 at 17:16):

I see tuples as numeric-index-labeled records.

view this post on Zulip jan kili (Dec 17 2024 at 17:16):

Ex: "Compilation Error: This record was created unlabeled, but here you reference a labeled field. Either reference this field by numeric index or add labels during its creation."

view this post on Zulip jan kili (Dec 17 2024 at 17:20):

{ x, y } # labeled record
{ x, "bar" } # unlabeled record
{ "foo", "bar" } # unlabeled record

{ x: Str, y: Str } # labeled record type
{ Str, Str } # unlabeled record type

view this post on Zulip Sam Mohr (Dec 17 2024 at 17:21):

And { x: 123, "bar" } is just invalid, presumably?

view this post on Zulip jan kili (Dec 17 2024 at 17:26):

And if we really want an unambiguous way to create an inline unlabeled record from other defs, that could be just three characters extra

{ 0: x, y }

but it could also just be contextual.

view this post on Zulip jan kili (Dec 17 2024 at 17:27):

(The syntax above inspired my {0 sigil idea, but I'm not advocating for that right now.)

view this post on Zulip jan kili (Dec 17 2024 at 17:36):

I'm struggling to think of an example where we'd strongly want a record constructed purely from other defs to be unlabeled lol

view this post on Zulip Brendan Hansknecht (Dec 17 2024 at 17:37):

I feel like it would add more rough edges where a beginner hits syntax that is frustrating or confusing with unusual wording (unlabelled record vs tuple and needing 0: in some cases).

To a new user, once they figure it out, I assume their question will be: "why this instead of normal tuples?".

view this post on Zulip Brendan Hansknecht (Dec 17 2024 at 17:38):

I'm struggling to think of an example where we'd strongly want a record constructed purely from other defs to be unlabeled lol

Most common is probably pattern matching. Also happens in return types on occasion

view this post on Zulip jan kili (Dec 17 2024 at 17:39):

We discussed in another thread that unlabeled records are poor choices for API values and function parameters, because naming things there is good, so I guess the 0: injection would be so rarely needed that we might not even teach it.

view this post on Zulip jan kili (Dec 17 2024 at 17:39):

For return types, whether its labelled or not is irrelevant, since you immediately destructure it.

view this post on Zulip Brendan Hansknecht (Dec 17 2024 at 17:40):

One is just more verbose for little gain in return types.

view this post on Zulip jan kili (Dec 17 2024 at 17:41):

{ x, y } = f x # f happens to return a labeled record
{ a, b } = g c # g happens to return an unlabeled record

view this post on Zulip Brendan Hansknecht (Dec 17 2024 at 17:41):

And if you want to return a tuple, you probably need to learn about 0:

view this post on Zulip Brendan Hansknecht (Dec 17 2024 at 17:41):

In your example, the first one only works if you want those exact names. It is relatively common to change the name (especially due to collisions from calling the same function more than once)

view this post on Zulip jan kili (Dec 17 2024 at 17:42):

So then you'd advocate for writing the function like g, which is fine

view this post on Zulip jan kili (Dec 17 2024 at 17:43):

g = \c ->
    a = "foo"
    { a, "bar" }

view this post on Zulip jan kili (Dec 17 2024 at 17:44):

You'd only need the 0: if every field is already named, like

g = \c ->
    a = "foo"
    b = "bar"
    { 0: a, b }

view this post on Zulip jan kili (Dec 17 2024 at 17:44):

Footgun?

view this post on Zulip Brendan Hansknecht (Dec 17 2024 at 17:45):

I don't think it is a footgun....too strong of a word. Just an inconvenience that new users will hit and be confused by.

view this post on Zulip Brendan Hansknecht (Dec 17 2024 at 17:46):

Will feel unnecessary when it could have been fully avoided by not overlapping with records

view this post on Zulip jan kili (Dec 17 2024 at 17:46):

If this is really the only encouraged use of tuples though, do they deserve their own primitive and symbols?

view this post on Zulip jan kili (Dec 17 2024 at 17:47):

They seem to fill a tiny gap in the language itself, which we could patch with something that already needs to exist elsewhere.

view this post on Zulip jan kili (Dec 17 2024 at 17:50):

I believe every other solid use of tuples wouldn't conflict with labeled record field punning, like when { foo, "bar", Baz } is (wait, idk if that's even real lol) nvm, but my question stands

view this post on Zulip Sam Mohr (Dec 17 2024 at 17:53):

They fill the gap left by arrays as well

view this post on Zulip Sam Mohr (Dec 17 2024 at 17:55):

I think Roc has been successful so far in prioritizing simple, easy-to-learn features that may overlap rather than prioritizing clever solutions. These two approaches to keeping Roc "simple" point to different definitions of simple, the first meaning "comprising low-complexity components", the second meaning "small number of components". I think the first definition is a better to strive for

view this post on Zulip jan kili (Dec 17 2024 at 17:55):

So fair

view this post on Zulip Sam Mohr (Dec 17 2024 at 17:56):

So if everyone and their grandma knows what a tuple is already, we need something easier to learn than them to fill the multiple holes that tuples plug IMO

view this post on Zulip jan kili (Dec 17 2024 at 17:57):

In the end, if Roc has 2-10x more parens and inline tuples become harder to read, I suppose that isn't a bad thing if inline tuples are conventionally discouraged - gap fillers only.

view this post on Zulip Richard Feldman (Dec 17 2024 at 17:58):

JanCVanB said:

If this is really the only encouraged use of tuples though, do they deserve their own primitive and symbols?

see #ideas > ✔ Do we need tuples? @ 💬 :wink:

view this post on Zulip Sam Mohr (Dec 17 2024 at 17:59):

I'd be open to putting a warning on using tuples where they shouldn't be used (I can only think of assigning tuples to a value without destructuring them as an undesired usage), but they fill a lot of gaps, so we seem to need them

view this post on Zulip jan kili (Dec 17 2024 at 18:01):

Yup, and above I only wanted to explore the difference between "need" and "which symbology should we overload"

view this post on Zulip jan kili (Dec 17 2024 at 18:02):

Because this seems to require either a sigil or Roc's sole overloaded bracket

view this post on Zulip Sam Mohr (Dec 17 2024 at 18:03):

I'm trying to take that into account. You and @Luke Boswell are ideas people, and I think Roc only got to where we are because of people like you. I don't want to depress ideation, it's very important. I just don't think the underlying problem is there in this case, otherwise I'd be right there with you

view this post on Zulip jan kili (Dec 17 2024 at 18:03):

If only there was a fourth ASCII bracket - why didn't they foresee four collection-y types lol

view this post on Zulip Sam Mohr (Dec 17 2024 at 18:04):

)new, tuple, syntax(

view this post on Zulip jan kili (Dec 17 2024 at 18:05):

{| oh, yeah, bebby|}

view this post on Zulip Richard Feldman (Dec 17 2024 at 18:05):

Ruby and Rust use pipes for lambdas, which is essentially a way of using them as brackets

view this post on Zulip Richard Feldman (Dec 17 2024 at 18:05):

not saying we should do that, just that there's predecent for it

view this post on Zulip Richard Feldman (Dec 17 2024 at 18:06):

as the "fourth bracket"

view this post on Zulip jan kili (Dec 17 2024 at 18:06):

I never want to be "that guy" who asks for table flips for minor inconveniences, so please (and thank you) continue to professionally hold our ideas to high standards.

view this post on Zulip Anthony Bullard (Dec 17 2024 at 18:10):

Richard Feldman said:

as the "fourth bracket"

Hope you never need to nest

view this post on Zulip jan kili (Dec 17 2024 at 18:10):

Also, general statement to anyone, if I'm ever "that guy" for any definition of "that", please let me know - DMs open too.

view this post on Zulip Anthony Bullard (Dec 17 2024 at 18:10):

JanCVanB said:

Also, general statement to anyone, if I'm ever "that guy" for any definition of "that", please let me know - DMs open too.

I think you’ve mostly been that cool guy and that’s it

view this post on Zulip Sam Mohr (Dec 17 2024 at 18:12):

I really like the look of the | character, and it's just as easy to type as parens (you have to hold shift for it). It wouldn't be a bad change IMO for function args, meaning we can leave tuples as-is

view this post on Zulip Anthony Bullard (Dec 17 2024 at 18:17):

A little dash of Ruby

view this post on Zulip Sam Mohr (Dec 17 2024 at 18:18):

or Rust :crab:

view this post on Zulip Anthony Bullard (Dec 17 2024 at 18:20):

I was appealing to maybe Richard’s nostalgia (maybe that has negative weight)

view this post on Zulip Anthony Bullard (Dec 17 2024 at 18:20):

I know NRI was a Rails app once upon a time

view this post on Zulip Sam Mohr (Dec 17 2024 at 18:20):

I hate Ruby, I'll be "that" guy

view this post on Zulip Sam Mohr (Dec 17 2024 at 18:20):

Though of course it has some nice stuff

view this post on Zulip Richard Feldman (Dec 17 2024 at 18:22):

I actually started #ideas > ✔ lambda syntax awhile back about pipe syntax for lambdas; you can read the thread to see how that went :big_smile:

view this post on Zulip Richard Feldman (Dec 17 2024 at 18:22):

tradeoffs might be different with parens-and-commas, not sure :shrug:

view this post on Zulip jan kili (Dec 17 2024 at 18:23):

Thank you Richard for being this forum's thread memory.

view this post on Zulip Sky Rose (Dec 17 2024 at 19:41):

So to summarize, options I've seen are:

view this post on Zulip Sam Mohr (Dec 17 2024 at 19:42):

Note: # currently is the comment prefix

view this post on Zulip Sky Rose (Dec 17 2024 at 19:42):

ah, shoot :face_palm:

view this post on Zulip Sam Mohr (Dec 17 2024 at 19:42):

I agree that the numeric implication makes it my frontrunner as well, but for this issue

view this post on Zulip jan kili (Dec 17 2024 at 22:44):

I agree but also it implies hashing, which nope


Last updated: Jun 16 2026 at 16:19 UTC