Stream: ideas

Topic: string interpolation syntax


view this post on Zulip Anton (Apr 17 2023 at 12:33):

"""
For string interpolation, \ feels worse, since that's where characters are most often escaped in other languages. It re-uses the symbol assigned for lambdas, which was also confusing for a bit. I personally would've liked to see Hello $(world) or Hello ${world}, I think there's value in being consistent here.
"""

view this post on Zulip Folkert de Vries (Apr 17 2023 at 12:38):

I'm also not a fan of \ for the simple reason that it breaks my vim editor tooling. The ( in \( is escaped and that breaks various shortcuts

view this post on Zulip Folkert de Vries (Apr 17 2023 at 12:38):

I quite like the rust/python {} approach. it's fairly simple and also easier to type than the \

view this post on Zulip Folkert de Vries (Apr 17 2023 at 12:39):

adding in a $ does not really seem needed to me, but I don't mind it either

view this post on Zulip Richard Feldman (Apr 17 2023 at 12:45):

but then you have to escape { and } instead

view this post on Zulip Richard Feldman (Apr 17 2023 at 12:46):

this has already concretely been a pain in glue generation in Rust, where whenever I copy/pasted a Rust or C code snippet into a Rust format! literal (because I wanted interpolation for things that were being generated), I had to replace all the {s with {{

view this post on Zulip Richard Feldman (Apr 17 2023 at 12:47):

the nice thing about \ is that it's already reserved (for character escapes) so it never requires extra escaping

view this post on Zulip Folkert de Vries (Apr 17 2023 at 12:49):

I'd say our use case was very non-standard

view this post on Zulip Richard Feldman (Apr 17 2023 at 12:52):

maybe another way of saying it is: \ has an objective benefit with nonzero real-world upsides: it doesn't require anything else to be escaped.

view this post on Zulip Richard Feldman (Apr 17 2023 at 12:52):

I think every other syntax alternative which doesn't have that objective benefit needs to clear the bar of "so much subjectively better that it outweighs that objective benefit"

view this post on Zulip Richard Feldman (Apr 17 2023 at 12:53):

personally {...} doesn't clear that bar for me (aesthetically I don't have a significant preference either way, honestly)

view this post on Zulip Richard Feldman (Apr 17 2023 at 12:53):

I'm open to others feeling differently, but that's where I think the bar is! :big_smile:

view this post on Zulip Folkert de Vries (Apr 17 2023 at 12:56):

for ${} only the specific combination ${ would need to be escaped as \${

view this post on Zulip Richard Feldman (Apr 17 2023 at 12:58):

true, although at that point ${...} is the same number of characters as \(...) except with needing a rare extra escape, so is that really so beneficial?

view this post on Zulip Richard Feldman (Apr 17 2023 at 12:58):

(I guess maybe for Vim shortcuts it is)

view this post on Zulip Folkert de Vries (Apr 17 2023 at 12:58):

for me, yes

view this post on Zulip Richard Feldman (Apr 17 2023 at 12:58):

yeah

view this post on Zulip Richard Feldman (Apr 17 2023 at 12:59):

as an aside, I prefer parens in the hypothetical future where we allow more expressions in there

view this post on Zulip Richard Feldman (Apr 17 2023 at 12:59):

so would $(...) work just as well?

view this post on Zulip Folkert de Vries (Apr 17 2023 at 12:59):

it also will look more familiar, though I don't think this is hard syntax to learn

view this post on Zulip Folkert de Vries (Apr 17 2023 at 12:59):

it gets trickier when you allow custom number formatting, but that is inherent

view this post on Zulip Richard Feldman (Apr 17 2023 at 13:01):

I'm skeptical of number formatting in string literal syntax being a good idea

view this post on Zulip Richard Feldman (Apr 17 2023 at 13:01):

I'm open to being convinced otherwise, but that's my general sense

view this post on Zulip Richard Feldman (Apr 17 2023 at 13:02):

like https://package.elm-lang.org/packages/coinop-logan/elm-format-number/latest/ seems like a better general strategy to me

view this post on Zulip Folkert de Vries (Apr 17 2023 at 13:03):

yes, probably

view this post on Zulip Folkert de Vries (Apr 17 2023 at 13:03):

those things are a bit like regexes

view this post on Zulip Folkert de Vries (Apr 17 2023 at 13:03):

you copy-paste, they cannot be understood, only written

view this post on Zulip Notification Bot (Apr 18 2023 at 12:13):

45 messages were moved from this topic to #ideas > display and log by Richard Feldman.

view this post on Zulip dank (Apr 24 2023 at 18:56):

i actually love the current way
it just aligns with how you actually do "non standard character stuff" (not claiming that newline isn't standard but you catch my drift)
i empathize the tooling aspect but perhaps there's an editor specific solution for it

view this post on Zulip Richard Feldman (Jan 05 2024 at 19:02):

Richard Feldman said:

so would $(...) work just as well?

the idea of switching to specifically this syntax has been growing on me over time. I now think it's worth considering as an actual change.

reasons:

"/users/\(username)/posts/\(postId)"
"/users/$(username)/posts/$(postId)"

it's even worse in Windows file paths (which are already bad enough to begin with due to having to backslash-escape all the backslashes):

"C:\\\\\(directory)\\\(filename)"
"C:\\\\$(directory)\\$(filename)"

view this post on Zulip Richard Feldman (Jan 05 2024 at 19:04):

also, with $(...) we could consider supporting $email (without parens) in the common case where you just want a particular variable and don't need parens to disambiguate, e.g. $(user.email) or $(Num.toStr num). Compare:

"/users/$(username)/posts/$(postId)"
"/users/$username/posts/$postId"

Actual currency always starts with a number (and variables can't start with numbers), so $1 would never interpolate. Also, since Roc is a type-checked language, if you wrote $user.email and forgot the parens, you'd find out at compile time (if user.email would have worked, then user is a record, so $user is a type mismatch because interpolated values must be strings), and of course if user wasn't defined at all, you'd find out about that at compile time too.

view this post on Zulip Richard Feldman (Jan 05 2024 at 19:05):

anyone have thoughts on this?

view this post on Zulip Brendan Hansknecht (Jan 05 2024 at 19:07):

Then you just have to escape when using $. Also, would unicode become $u(123)

view this post on Zulip Richard Feldman (Jan 05 2024 at 19:07):

I think Unicode escape syntax can be considered separately from string interpolation; after all, it is in most languages

view this post on Zulip Richard Feldman (Jan 05 2024 at 19:07):

Brendan Hansknecht said:

Then you just have to escape when using $.

what would be an example of when this would come up though?

view this post on Zulip Richard Feldman (Jan 05 2024 at 19:08):

like you'd have to specifically want to write $( (or in the case of the $user idea, $ followed by a non-number) to have to escape $

view this post on Zulip Brendan Hansknecht (Jan 05 2024 at 19:09):

I would assumed you just always escape it for consistency. Otherwise it would be a compile warning.

view this post on Zulip Richard Feldman (Jan 05 2024 at 19:10):

oh I wouldn't think so - I don't see people doing that in the languages which use $ for interpolation, so I assume they wouldn't in Roc either :big_smile:

view this post on Zulip Richard Feldman (Jan 05 2024 at 19:13):

to put it another way, I would assume that if we made this change, everyone would change all the \( to $( and that would be the only change anyone would (or would have to) make

view this post on Zulip Richard Feldman (Jan 05 2024 at 19:13):

unless we also did the $name thing, in which case people might optionally choose to shorten some of the interpolations (or maybe roc format would make that change automatically)

view this post on Zulip Brendan Hansknecht (Jan 05 2024 at 19:15):

ok

view this post on Zulip Brendan Hansknecht (Jan 05 2024 at 19:16):

In that case, only other thought is that we may use $ for shadowing. So how would you interpolate a shadowed value and would that look really bad. Long form $($buf), short form $$buf?

view this post on Zulip Richard Feldman (Jan 05 2024 at 19:19):

the current proposal is that if you have $user in scope, you can't also have user in scope

view this post on Zulip Richard Feldman (Jan 05 2024 at 19:19):

which means that $user in interpolation could unambiguously refer to either $user or user, whichever was in scope

view this post on Zulip Richard Feldman (Jan 05 2024 at 19:20):

but also I think it's fine if $($user) was required, since the goal of the shadowing experiment is to make it be opt-in and slightly higher-friction than not using shadowing anyway

view this post on Zulip Brendan Hansknecht (Jan 05 2024 at 19:24):

Sounds good. Yeah. I approve of the change

view this post on Zulip Richard Feldman (Jan 05 2024 at 19:37):

@Joshua Warner can you think of any parsing implications for this change? (positive or negative)

view this post on Zulip Luke Boswell (Jan 05 2024 at 19:52):

If your string is like "hello,$user.name!" is the .name part a part of the variable being interpreted or the string?

view this post on Zulip Luke Boswell (Jan 05 2024 at 19:53):

Like how do you know where the variable ends and string starts again?

view this post on Zulip Luke Boswell (Jan 05 2024 at 19:54):

Oh, wait this isnt permitted and would be a type error. Got it.

view this post on Zulip Luke Boswell (Jan 05 2024 at 19:54):

I like this idea a lot!

view this post on Zulip Isaac Van Doren (Jan 05 2024 at 20:18):

I like the idea of using $(), but am less keen on only having interpolation without parens. Even if the rules for just $ are unambiguous, it seems like a source of confusion. For example, it might be surprising to see that “$foo” interpolated, but then “$1” did not. To me, adding parentheses is so negligible that it doesn’t seem worth making things more confusing to avoid typing them occasionally.

view this post on Zulip Isaac Van Doren (Jan 05 2024 at 20:20):

Then you would also end up in more situations where you have to go back and add parentheses where you thought you wouldn’t need them. For example, if you need to change “$foo” to $(Num.toStr foo)”. I would rather not have the option to use $ vs $() and instead always be consistent necessarily.

view this post on Zulip Isaac Van Doren (Jan 05 2024 at 21:23):

I doubt this would come up much, but you would also have weirdness for any strings like this “ca$h”

view this post on Zulip Kevin Gillette (Jan 05 2024 at 21:23):

Richard Feldman said:

only Swift and Roc currently use \ instead)

And CUE. There are probably others as well.

I do rather like \(...). I don't think there really needs to be a distinction between the character used for traditional "literal" escapes, and the character used to for interpolation. Having them be the same means that I can paste more strings successfully without needing to worry about all the potential interpretations that might have.

\ has a lot of unused range in terms of the character that follows it. \(...) is just making good use of that range.

view this post on Zulip Kevin Gillette (Jan 05 2024 at 21:27):

In terms of editor misinterpretation, that can get solved with tooling improvements (like with treesitter support for Roc).

I use vim as well, but it gets confused even in mainstream languages whenever I have '{' (i.e. a string or char containing a brace), so I won't claim its brace/parens tracking is at all sophisticated. Thus I don't think that minor editor blunders are a compelling reason to change the language.

view this post on Zulip Richard Feldman (Jan 05 2024 at 22:57):

what do you think about the other reasons?

view this post on Zulip Isaac Van Doren (Jan 06 2024 at 00:32):

Another thought about using $ alone: it means that there would need to be a rule to determine which interpolation to use here

x = "hello"
xy = "world"
"$xyz"

The especially undesirable part of this is that if I removed either x or xy, the value being interpolated would silently change. I would much prefer it to be explicit and cause an error.

view this post on Zulip Joshua Warner (Jan 06 2024 at 00:44):

I don't think using $() instead of \() meaningfully changes the parser/lexer. If we didn't allow parens (i.e. only $xyz), that would make doing lexing separately somewhat easier.

view this post on Zulip Joshua Warner (Jan 06 2024 at 00:54):

I like the symmetry of the current \() with other escape sequences, but that's a pretty minor preference.

view this post on Zulip Joshua Warner (Jan 06 2024 at 00:54):

it's even worse in Windows file paths (which are already bad enough to begin with due to having to backslash-escape all the backslashes)

Lol windows paths are the worst

view this post on Zulip Kevin Gillette (Jan 06 2024 at 08:24):

Richard Feldman said:

what do you think about the other reasons?

I think the familiarity point has some merit, but I don't think it's a steep learning curve difference either way. The approachability (or not) of Roc isn't going to boil down to just choice of interpolation syntax, and of course is dependent on programmer background.

I suspect ${...} will feel more familiar to most readers for simple expression interpolation. It's true that bourne shells have both ${...} and $(...), though the former is probably the closer analogue to what most uses of Roc interpolation will look like.

I agree that $ is more visually distinctive/readable in dense strings compared to \. In strings with more spacing, there is not much readability difference.

view this post on Zulip Kevin Gillette (Jan 06 2024 at 08:31):

I believe we should be cautious about $user style interpolation. Speaking of shells, some bash style guides explicitly discourage this shorthand style in favor of explicitly-braced interpolation.

We can always introduce the shorthand syntax later if we determine it's worth the tradeoffs, after gaining experience with the longer, delimited syntax.

view this post on Zulip Richard Feldman (Jan 06 2024 at 11:32):

yeah the difference in strings with slashes in them is the biggest usability delta to me, and it comes up often when working with paths or URLs

view this post on Zulip Richard Feldman (Jan 06 2024 at 14:25):

PR to introduce $(...)

view this post on Zulip Agus Zubiaga (Jan 06 2024 at 18:15):

An admittedly minor benefit is that $() is a tiny bit easier to type as you can just stay at the numbers row with the shift key pressed

view this post on Zulip Agus Zubiaga (Jan 06 2024 at 18:16):

Think about the kilometers of finger travel saved :big_smile:

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

in the parens-and-commas world, it might make sense to adopt the much more popular ${...} interpolation syntax, to avoid repeated parens.

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

with whitespace calling, $(Num.toStr foo) is nice, but with parens-and-commas, I think $(foo.to_str()) is less nice than `${foo.to_str()}, especially if the curly braces can be syntax-highlighted differently from the parens

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

anyone have thoughts on that?

view this post on Zulip Brendan Hansknecht (Dec 22 2024 at 18:05):

Sounds reasonable...though not particularly major either way. Also, would we consider just {} like in a number of other languages.

view this post on Zulip Brendan Hansknecht (Dec 22 2024 at 18:05):

So just {foo.to_str()}

view this post on Zulip Norbert Hajagos (Dec 22 2024 at 18:09):

{} and ${} both seem better with PNC. I like {} more, haven't found an instance where the extra $ was helpful.

view this post on Zulip Richard Feldman (Dec 22 2024 at 19:38):

my main thoughts on $ vs. just braces are:

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

I guess I am used to rust, python, and zig just using {}. I guess I don't print { too often so mostly don't notice.

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

No real opinion on dollar otherwise

view this post on Zulip Joshua Warner (Dec 22 2024 at 19:49):

I was always slightly preferential to the backslash. Nice symmetry with other escapes in strings.

view this post on Zulip Joshua Warner (Dec 22 2024 at 19:51):

Just using plain curlies is annoying anytime you’re outputting code, json, etc.

view this post on Zulip Richard Feldman (Dec 22 2024 at 19:55):

if we didn't have $, what would be the syntax for escaping curlies? backslash curly?

view this post on Zulip Joshua Warner (Dec 22 2024 at 20:00):

Yeah backslash seems the obvious choice under those constraints. But $ doesn’t seem terrible to me either

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

I guess an argument for braces alone is that it makes the common case nicer and more concise at the cost of having the edge case be annoying

view this post on Zulip Richard Feldman (Dec 22 2024 at 20:46):

and I guess for normal application development in Roc, outputting code should be rare and things like JSON should be done with a library anyway

view this post on Zulip Kilian Vounckx (Dec 22 2024 at 22:17):

Another option, I honestly don't know which is better, is to have 2 different string types like python. There, only 'f-strings' interpolate. So the literal f"Hello, {name}" would interpolate the variable name. However the literal "Hello, {name}" would not.
I think JS has a similar thing with backticks vs quotes for string literals

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

I have a strong preference for only having one string syntax :big_smile:

view this post on Zulip Kilian Vounckx (Dec 22 2024 at 22:38):

Fair. I would say I have a slight preference for $ or \ in front of the braces in that case, to avoid having to escape otherwise

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

\ had the problem of making paths look confusing, e.g. /foo/bar/\{baz}/blah so I don't think we should go back to that :big_smile:

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

to me, it's between ${ ... } and { ... }

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

based on how annoyed I've been with braces-only in the Roc code base, I'd prefer ${ ... } but I'm not sure how much of that is just due to the coincidence of writing a compiler in Rust

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

it isn't annoying at all at Zed, for example

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

I do kinda like the idea of always using ${ ... } and just never even thinking about escaping

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

because needing to escape never comes up

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

I guess Ruby syntax would also be fine, e.g. #{ ... }

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

which would have the upside that # is already a character used in Roc syntax, which could have some lexing performance benefits, although:

view this post on Zulip Richard Feldman (Dec 22 2024 at 23:00):

overall ${ ... } seems like the nicest option to me, because it means you basically never have to even think about escaping, and it's more familiar to more beginners than #{ ... }

view this post on Zulip Richard Feldman (Dec 22 2024 at 23:00):

does anyone feel strongly that we should do something different?

view this post on Zulip Sam Mohr (Dec 22 2024 at 23:39):

I am down for the consistency of ${...} for the reasons you describe

view this post on Zulip Isaac Van Doren (Dec 23 2024 at 03:14):

My preference is also for ${...}

view this post on Zulip Richard Feldman (Jan 05 2025 at 18:00):

ok cool, let's go with ${ ... }!

view this post on Zulip Richard Feldman (Jan 05 2025 at 18:00):

I'm working on a blog post to announce purity inference, and I'll use it in that (since that blog post will also use parens-and-commas as well as snake_case)

view this post on Zulip Richard Feldman (Jan 05 2025 at 18:01):

I'm assuming it'll be a quick enough change that we can get it in before the blog post is done anyway :big_smile:

view this post on Zulip Richard Feldman (Jan 05 2025 at 19:05):

(also I want to hold off on publishing it until we've removed Task from builtins, and other related cleanups)

view this post on Zulip Brendan Hansknecht (Jan 05 2025 at 19:31):

Fully remove task or deprecate it and update the core platforms to PI.

view this post on Zulip Luke Boswell (Jan 05 2025 at 19:35):

I think we should fully remove it. It's going to be very confusing for people if we keep it in the builtins and Tutorial.

view this post on Zulip Brendan Hansknecht (Jan 05 2025 at 20:16):

I assumed we'd keep it around for a bit as other platforms migrate, but I'm always happy with ripping things out sooner

view this post on Zulip Anton (Jan 06 2025 at 11:23):

I'm good with a full removal as well


Last updated: Jun 16 2026 at 16:19 UTC