Skip to content

String interpolation elements #1478

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
lrhn opened this issue Feb 26, 2021 · 5 comments
Open

String interpolation elements #1478

lrhn opened this issue Feb 26, 2021 · 5 comments
Labels
feature Proposed language feature that solves one or more problems

Comments

@lrhn
Copy link
Member

lrhn commented Feb 26, 2021

String interpolations are great, but they could be even greater by taking inspiration from collection literals.

We currently allow any expression inside an interpolation block (${...}). If we instead treated interpolations like elements of a collection of Strings, then we could allow for, if and spread elements too.

Example (one with everything):

bool sep = false;
var jsonText = """
{
${for (var key in map.keys)
  if (!key.startsWith('_')) 
    ...[if (sep == (sep = true) ",\n", 
        '  ', key, ":", map[key]]}
}
""";

One use-case I often run into is optionally adding some text to the string:

  "example${name != null ? "($name)" : ""}: something"

Here the : "" is annoying and superfluous. With if-interpolation we can write it as:

  "example${if (name != null) "($name)"}: something"

May even want to allow a comma-separated sequence of interpolation elements inside an interpolation:

 "example${if (name != null) "($name)", if (age != null) "age: $age"}"

Not technically necessary, one can always just start a new interpolation, but , can be shorter than }${.
May have to consider how well it reads. (And whether it means that we allow zero or more elements, so ${} would be valid. I have no problem with that, when one can write ${if (false) 0} or ${?null} anyway.)

Today you can use list+join.
That has the advantage of also being able to add a separator.

var jsonText = """
{
${[for (var key in map.keys)
   if (!key.startsWith('_'))
     "  $key: ${map[key]}"].join(",\n")}
}
""";

There is no similar concept of a join with separator in elements.

@lrhn lrhn added the feature Proposed language feature that solves one or more problems label Feb 26, 2021
@lrhn lrhn changed the title Improved string interpolation String interpolation elements Jun 27, 2024
@ghost
Copy link

ghost commented Jun 28, 2024

In code generation (which includes json) it would be more convenient to introduce a special kind of literal, where the compiler removes all whitespace, but allows \n, \s, and \ns (\4s inserts 4 spaces)

var jsonText = code"""{\n
  $for (var key in map.keys) 
     $if (!key.startsWith('_')) 
        \2s $key: ${map[key]} ,\n
     $end
  $end
}
""";

Another (Latex-inspired) variant:

var jsonText = code"""{\n
  \for (var key in map.keys) 
     \if (!key.startsWith('_')) 
        \2s $key: ${map[key]} ,\n
     \end
  \end
}
""";

@lrhn
Copy link
Member Author

lrhn commented Jul 1, 2024

Or just do:

var first = true;
String sep() {
  if (!first) return ",";
  first = false;
  return "";
}
var jsonText = """{${
  for (var key in map.keys) 
     if (!key.startsWith('_')) 
        "${sep()}\n  $key: ${map[key]}"
}
}""";

(Adding the correct commas is still the biggest issue. Probably want to create a helper class for it. Or take this as another reason for never generating JSON text manually.)

@ghost
Copy link

ghost commented Jul 1, 2024

The advantage of any of these forms over currently used idiom [...].join() is not obvious. What is it? Performance?

@lrhn
Copy link
Member Author

lrhn commented Jul 2, 2024

Mostly performance, yes.
No need to create the list, just to throw it away again, and no need to create the intermediate string from .join(), it can just all be added to the underlying StringBuffer (or whatever) and joined only once at the end.

The list is not part of the operation, it's just an accident of implementation because it's the easiest way to introduce separators automatically. That means that it is mostly noise when reading. The reason for it existing, the join call, comes after you needed to know why the list is there, which is a slight stumbling block for readability.

@ghost
Copy link

ghost commented Jul 2, 2024

An accident indeed, and a happy one at that 😄

For anyone familiar with the idiom, it's quite readable. (To me, it feels more readable than the alternatives.)

Performance-wise, the compiler can intrinsify it , thus avoiding an extra allocation, and/or implement other optimizations. There might not be a whole lot of overhead in proportion to other expenses in the above example anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Proposed language feature that solves one or more problems
Projects
None yet
Development

No branches or pull requests

1 participant