String interpolation

Topics: C# Language Design, VB Language Design
Coordinator
Apr 2, 2014 at 12:31 AM
Edited Apr 2, 2014 at 11:08 PM

String interpolation

String interpolation is one of the top ten requests on uservoice - http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2312991-string-interpolations-in-c

We've been discussing the feature back and forth in C# and VB language design meetings. This thread starts with a summary of the points raised from the Visual Basic Language Design Meeting of 2013-12-09, combined with comments from MVPs, but I'm hoping that everyone who's interested in the topic can add their thoughts and suggestions to the thread. (I switch back-and-forth between C# and VB syntax, which is how we did our discussions...)

So far the consensus seems to be a resounding "meh" because the proposed designs seem to miss the mark, and the feature isn't as important as other potential new features. Here's a writeup of all our notes so far, so we can have a wider discussion with the general public.

Examples

Here are some examples in C# and VB:
var x = "It measures \{distance:0.00}m";
Dim filepath = $"{drive}:\{path}\{file}.{ext}"
Dim query = $"{url}?arg1={x}&arg2={y}"
Dim csv_line = $"{name},{dollars}{vbCrLf}"
String interpolation would be syntactic sugar for a call to String.Format:
var x = "It measures \{distance:0.00}m";
==>
var x = String.Format("It measures {0:00}m ", distance);

Motivation

Why string interpolation? Isn't String.Format already good enough? More generally, what is the real problem that string-interpolation tries to solve? Here are some possible answers, both good and bad. I think we haven’t yet got a crisp good answer to this question. Some possible answers:
  1. It is a full replacement from String.Format, with culture and everything, to solve the mess that you find when there are too many positional arguments (see examples below)
  2. It provides a quick-and-dirty way to do string concatenation in the invariant-culture for use in composing strings together to pass to other APIs
  3. It provides an easy way to stick in identifiers
  4. It is an easier way to teach Coding 101 – don’t want to start beginners on String.Format
  5. All the other cool languages have it. (bad reason)

Motive 1: a replacement for String.Format

Proposal: "String interpolation is a replacement for String.Format, for cases where the "positional" arguments {…} of String.Format are confusing to read." As a motivating example, the following code has a few bugs (missing space before {5}, and the orders have got mixed up) and it's not really human-readable…
Dim x = String.Format("Robot {1} reporting{0}  {3} levels are {2}{0}" &
                      "{5} levels are {4}",
                      vbCrLf, name, coolant.name, coolant.level,
                      reactor.name, reactor.level)
Isn't normal string concatenation already good enough? Again, sometimes it's confusing. The bug here is that there's a missing vbCrLf at the end of the second line:
Dim z = "Robot " & name & " reporting " & vbCrLf &
        "  " & coolant.name & "levels are " & coolant.value &
        "  " & reactor.name & " levels are " & reactor.value
One curious proposal is to allow string concatenation just by omitting the & in VB (or + in C#), just as is currently the case in C for string literals. But this seems pretty limited -- wouldn't allow for format specifiers, would only work for expressions with string literals before and after them, might close the language off to context-specific keywords in the future, and doesn't look very readable.


String interpolation could be neater and easier to debug than either String.Format or concatenation...
    Dim y = $"Robot {name} reporting
{coolant.name} levels are {coolant.level}
{reactor.name} levels are {reactor.level}"
However, this example is fishy. Most professional programmers won't be writing user-facing strings in code. Instead they'll be storing those strings in resources (.resw, .resx or .xlf) for reasons of localization. So there doesn't seem much use for string interpolation here.

Motive 2: a quick-and-dirty way to do construct strings for APIs.

Dim filepath = $"{drive}:\{path}\{file}.{ext}"
Dim query = $"{url}?arg1={x}&arg2={y}"
Dim csv_line = $"{name},{dollars}{vbCrLf}"
These examples seem to work nicely with string interpolation, but we'd be opening up potential security holes when people construct strings without sanitizing them first. That's a worry.
Coordinator
Apr 2, 2014 at 12:46 AM
Edited Apr 2, 2014 at 11:08 PM
Some open questions about string interpolation...

Q. Default culture?

What should the default culture be for string interpolation?
  • String.Format uses CultureInfo.CurrentCulture by default
  • It feels like InvariantCulture would be a better bet if users are typically using string-interpolation as a way to construct strings to pass to APIs.
  • CurrentCulture would be make more sense only if (1) people use it mainly for UI strings, (2) and they’re not localizing their UI by storing strings in .resw
  • CurrentCulture also makes sense if the feature feels really close to String.Format.

Q. Specify culture?

Should users be able to specify the culture to be used?
  • The Javascript proposal (next post) would allow this, since you could call
Dim s = FmtInvariantCulture`hello {x}`
Dim s = FmtCurrentCulture`hello {x}`
  • If we want to use a prefix for string interpolation var x = $"hello";, then that provides a place where culture could be specified var x = ${culture}"hello";
  • Alternatively, culture could be specified at the end, e.g.
var x = "hello \{x}"(CultureInfo.CurrentCulture);
Dim x = $"hello {x}"(CultureInfo.CurrentCulture)
  • MVP Bill McCarthy suggested this interesting syntax:
Dim x = $"hello {x}" Using CultureInfo.CurrentCulture

Q. Perf of String.Format ?

What kind of compiler optimizations would be used?
  • If the compiler sees that it can optimize away the call to String.Format, and use instead something more lightweight (that still has the same semantics), then it would:
var x = "It measures \{distance}m";
==> var x = String.Format("It measures {0}m ", distance);
==> var x = String.Concat("It measures ", distance.ToString(), "m");
  • Will there be compiler folding? e.g.
var x = "hello \{a}" + "world \{b}";
==> var x = "hello \{a}world \{b}";
==> var x = String.Format("hello {0}world {1}", a, b);
  • Will the compiler do compile-time evaluation if the arguments that go in the hole are constant and which don’t depend on the current culture? It seems risky to take a dependency on the internal behavior of String.Format…

Q. Prefix syntax?

On an interpolated string, should it have a prefix?
var x = "It measures \{d}m";
var x = $"It measures {d}m";
  • For C# we were leaning towards the first form, where the presence of \{.} inside a string implies it is to be interpolated. For VB that's impossible, so we were pushed towards the second form where the prefix implies that the string will be interpolated.
  • MVPs raised the issue that, if you wanted to do a global search to find all interpolated strings in a codebase, the prefix form makes it easier. If you merely searched for { then you'd get false-positives from double-escaped \{, and from { in XML doc-comments.
  • For choice of prefix, we considered the following, and preferred $
Dim x = @"hello"   ' bad, since confusing with C# verbatim strings
Dim x = #"hello"   ' used in a few other languages
Dim x = $"hello"   ' used in Nemerle. Fits with the VB heritage

Q. Hole syntax?

In an interpolated string, what syntax to use for marking an expression hole? And then how do you escape that syntax if you want to use it literally? For VB we considered the following:
Dim brace  = $"hello {name} "" {{ {vbCrLf}"    ' PHP, Python, String.Format
Dim escape = $"hello \{name} \" \\ \n"         ' C# proposal
Dim hash   = $"hello #{name} "" ## ${vbCrLf}"  ' Ruby, Coffeescript
Dim $brace = $"hello ${name} "" $$ ${vbCrLf}"  ' Dart
Dim $paren = $"hello $(name) "" $$ $(vbCrLf)"  ' Nemerle
Dim $dollar= $"hello $name "" $$ $vbCrLf"      ' PHP, Perl, Dart, Nemerle
  • brace is strongly reminiscent of String.Format, which is good. And {{ is how you already escape out of String.Format. The "vbCrLf" is reasonable way to get a newline. We preferred this option for VB.
  • escape has the nice feature that it opens the door for escape characters, and is like C#. But it feels wrong to have to have that redundant extra character.
  • hash doesn’t have much going for it.
  • $brace re-uses $ in a reasonable way but the way to escape $ is pretty ugly
  • $paren is a Nemerle variant of $brace with the same disadvantages
  • $dollar would be a nice shortcut for the common case of just wanting to include an identifier, and is something allowed by string-interpolation in a lot of other languages. However it only combines well with $brace and $paren.

Q. Expressions in holes?

What kinds of expressions should be allowed in the "holes"? Just identifiers? Or qualified identifiers? How about indexers? Invocations? Arbitrary expressions?
var x = "This is \{arg[0]} and \{arg[1]}";
var y = "This is \{Lookup(x)} ";
var x = "Welcome \{user.Name} to \{user.Activity}";
  • MVPs justifiably observed that it would be bad practice to use complex expressions inside the holes.
  • But it's hard to draw a clear compiler-enforced line between "things that are okay in holes" and "things that aren't". For instance, indexers and method-invocations are probably good. So to keep things plain and regular, the compiler should probably to allow all expressions.
  • There's a good precedent for this. With the async/await feature, we wanted to allow things like var x = await t and also using (var x = await t). And so we allowed await in all expression contexts, even though it's bad practice to use them in confusing places like x[await t1] += await t2;
  • In practical terms, it's very hard (in terms of implementing the lexer+parser) to allow arbitrary expressions inside a hole. VB already does this for expression holes in XML-literals, so could use the same codebase for expressions in interpolated-string-holes. But C# doesn't yet do this anywhere.
  • Should it allow format-specifiers like the :0.00 used in the first example? That might complicate the lexing/parsing. But we don't see any ambiguity problems.

Q. C# verbatim string?

Can you use string interpolation in a C# verbatim string?

Q. VB implicit line continuations?

How does it work with VB implicit line continuations? e.g.
Dim x = $"hello { 3
         + 4 } world"
Answer: it works exactly the same as always: implicit line continuations happen before or after specific characters. The fact of being inside a hole in an interpolated string doesn’t change that. So the above code won’t work, since implicit line continuation isn't allowed before +.

Also, { allows an implicit line-continuation after it as always, and } at the end of a hole allows an implicit line continuation before it as always. Note for precedent that Dim x = New With { .x=15} also allows an implicit LC after the {, and XML expression holes allow an implicit LC after the <%=.

Q. VB Case statement?

Can you use interpolated strings as a VB Case expression? e.g. Select Case x : Case $"hello {a}
Answer: Yes. VB case statements always allow computed values here. C# never does.

Q. Const?

Is an interpolated string const? Const x as String = $"hello"

Answer: No, never, not even for the ones that are clearly recognizable as constants. The rule is that string interpolation is semantically shorthand for String.Format (notwithstanding any under-the-hood compiler optimizations).
Coordinator
Apr 2, 2014 at 12:49 AM
Edited Apr 2, 2014 at 11:10 PM
Some alternative approaches to string interpolation...

The Python alternative

There's an idiom used in Python today, that some people in VB/C# are also adopting. Here's how it would look in .NET:
Dim args = New With {.Name = "Jones", .Address = "1256 92nd St"}
Dim str = "Mr {Name} lives at {Address}".FormatEx(args)
The idea here is that it's all the positional format-arguments {0}, {1} in String.Format that are difficult to read. So we could do them using named arguments {Name}, {Address}. You would write an extension method on String which takes an object. At runtime your method parses the string to find curly braces (just like String.Format does), and it uses reflection to look for corresponding members in the "args" object.

(You'd have to be careful that the folks doing localization of your strings don't try to localize {Name} or {Address} !)

With the proposed "dictionary" features of C# and VB vNext, you could write FormatEx using dictionary arguments rather than reflection:
Dim args = New Args With {!Name = "Jones", !Address = "1256 92nd St"}
Dim str = "Mr {Name} lives at {Address}".FormatEx(args)
This Python-style approach would also solve the culture problem -- because you can easily pass Culture to your extension method.

The Javascript alternative

There's a string-interpolation proposal on the table for the next version of EcmaScript that's designed specifically to solve the security worries of string interpolation. It also solves Culture.
http://wiki.ecmascript.org/doku.php?id=harmony:quasis

It would look something like this if we translated it into .NET:
Dim x = c.Fmt`Mr {name} at {address}`
==>
Dim x = c.Fmt({"Mr ", " at ", ""}, name, address)
The idea here is that at compile-time the interpolated string gets broken up into pieces, and then at runtime it gets passed as an argument to the specified function or delegate. The advantage, as compared to the Python approach, is that there's less runtime overhead.



We had discussed something sort-of-related for C#. "There are many different APIs which work the same way: let's come up with a proposal for string-interpolation which works with all of them". It would expand an interpolated string into a comma-separate sequence of expressions :
$"hello {a} in {b}"
==>
"hello {0} in {1}", a, b
This would enable it to be used for a wide variety of APIs, e.g.
1: Console.WriteLine($"hello {a} in {b}")
2: Dim x = String.Format($"hello {a} in {b}")
3: Diagnostics.WriteLine($"hello {a} in {b}")
However it would prevent another idiom,
4: Dim x = $"hello {a} in {b}"
The pain of losing [4] is severe. And the other cases above all boil down to a call to String.Format, so we're not losing much by saying that string-interpolation is always translated into a call to String.Format. Indeed, we think that most of the time people take parameters (fmt as string, ParamArray args as Object()), then they just pass it down straight to String.Format. True there are just a few cases where they don't... for instance, the Roslyn codebase has a function where it modifies the format and args before passing them to String.Format. The pain of losing these scenarios seemed less than the pain of losing [4].
Apr 3, 2014 at 5:43 PM
I cannot catch what's wrong with "interpolation string" (InStr) and why I read all that blah-blah of strange arguments.
InStr is quite simple and doesn't require to wrinkle your forehead.
  1. InStr IS NOT a replacement or sugar for string.Format! It's just another feature to quickly insert values into string. String.Format is just ugly, verbose and tricky for such simple task.
  2. Since main goal of InStr is simplification of operation, it should look same simple. So Nemerle-like syntax is the best: $"My name is $name!". If you're not scary to go further, quotes also can be more distinguishable, say 'backquotes': var s = name=$name;
    Escape sign? Same dollar! My salary is $$$salary. For complex (and rare) cases {} are the limit: Name = ${person.FirstName}.
    Related culture and other sh*t most of us never worry about: FORGET. Just make 'this_piece_of_data.ToString()' and put it into my string. Period.
    So how long we will piss around for things other languages have from the box?
    We're NOT building an 'ideal language' (esp. when C# is already far from it), we just need PRACTICAL FEATURE. If you write real code (instead of writing language bells-and-whistles), you meet this string interpolation in billion places! That's why people ask for implementing feature and not discussing "oh, it looks like string.Format!".
So when this goddamn simple thing will appear?
Coordinator
Apr 4, 2014 at 3:26 AM
Hey Vincent3000, you're right, it's important to always remember that the "bread-and-butter" simple common cases have to remain simple. Yours is a strong vote for the Nemerle $.

For the semantics, if you're just calling ToString(), that's exactly what String.Format does! ToString() uses the current culture. So what you've described has exactly the same effect as syntactic sugar for String.Format in the current culture, but with one differences...
  • String.Format works when an argument is null (it uses an empty string). Do you mean you'd prefer to throw a NullReferenceException for "$name" when name is null?
...

On a more general note we’d appreciate it if posters take into consideration that this forum is read by people of all backgrounds. Please everyone ensure your posts are polite and contain no inflammatory or aggressive language that some readers may find offensive. Thanks!
Apr 4, 2014 at 11:49 AM
Are you aware an implementation (sort of) already exists. See Here
Apr 4, 2014 at 2:11 PM
There is a .net library project serilog, which implements some kind of string interpolation to allow to store message (formats) in some kind of resource dictionary (think of event log message string resources) and execute them againt a collection of objects.

It does a lot of things (enumerations join, force stringification, object dump), that are certainly out of scope for this feature, but perhaps there are some interesting goals and ideas that could deserve a read.
Apr 5, 2014 at 9:34 AM
Edited Apr 5, 2014 at 12:18 PM
I must say that this a very important feature in my opinion.

I think "most professional programmers won't be writing user-facing strings in code" is missing the point -- there are a lot of cases when similar examples are OK:
  1. Any logging. I can't count all the times I used Info/Debug/etc that all use string.Format internally. It is very easy to make a mistake in the code, and while tests help it is hardly perfect, especially when someone writes LogFatal and later that does not log because there is {2} instead of {1}.
  2. Exceptions -- it is often useful to have detailed exception messages, however user does not normally see those, so unless it is a framework or a very popular library there is little reason to localize them.
  3. Non-enterprise code. Minor libraries, tools and other (often OSS) projects may not consider localization within their scope.
  4. Enterprise non-user-facing code -- in places where I worked there was a lot of internal dev tools and utilities that did not require localization.
And here are some more examples that benefit from more advanced interpolation scenarios (ES-like):
  1. Serilog, as mentioned by styx31 -- it could get proper arguments instead of having to analyze string dynamically.
  2. SQL -- ES-like implementation would allow arguments to be analyzed/passed as command parameters.
I would love ES-like solution (with better syntax though), but even a straightforward solution would be really good.
Apr 5, 2014 at 11:02 AM
Edited Apr 5, 2014 at 11:06 AM
AdamSpeight2008 wrote:
Are you aware an implementation (sort of) already exists. See Here
That's not quite the same. Scott Hanselman's code is limited to interpolating properties of a single object.

This proposed feature would allow you to interpolate on things like local variables, their properties or results of functions (depending on what answer is selected for the Expressions in holes? question above).
Apr 5, 2014 at 1:05 PM
Edited Apr 5, 2014 at 1:06 PM
Some opinions:
  1. Allowing all expressions in the hole seems to be the best solution for a language. Some people may misuse that, but that's true for any feature.
  2. Format specifiers seem like a good idea as long as ambiguity is not a problem. Should it be an error if the expression is not statically IFormattable or has no ToString(string) overload?
  3. Instead of allowing this in @ strings, would it be possible to allow multiline in standard strings?
For ES-like solution, would it be a good idea to have something like
public class InterpolatedString {
     public string Template { get; } /* string.Format compatible template */
     public object[] Values { get; }  /* values passed into holes */
     public string ToString() /* default implementation, does string.Format */
}
?

Then you can generate it based on the context, same as with Expression<Func>.
This would play really nice with libraries (and extension methods?), but I am not sure if that is too complex for things like overload resolution.
Apr 5, 2014 at 2:03 PM
Edited Apr 5, 2014 at 2:10 PM
When I look at the following two alternatives:
"My name is " + Name + " and I am " + age + " years old."
"My name is \{Name} and I am \{age} years old."
I notice the first one already allows arbitrary expressions in holes while the second one probably has problems with expressions containing literal strings or block lambdas.

It seems to me that whatever approach is taken, the hole should either explicitly not accept arbitrary expressions, or the arbitrary expression should be outside of an enclosing syntax element such as double-quotes or curly brackets.

Something like the following might be conceivable:
"My name is "{Name ?? "<missing>"}" and the youngest in my team is "{team.Min(p => { return p.Age; })}" years old."
with the idea here being that from the point of view of lexing, the contained expression is outside of the literal string, while a grammar rule would cause the parser to reconstruct the complete string for the compiler to generate a call to string.Format. That way the lexer would no longer need to support arbitrary nesting.

(Please do not comment to tell me that the lambda expression can be written without curlies; I am well aware of that. It’s an example and it’s valid code that would have to work.)

If you do want to allow arbitrary expressions inside \{ ... }, the lexing rules would get complex. In the expression
"My name is \{Name ?? "<missing>"} and the youngest in my team is \{team.Min(p => { return p.Age; })} years old."
the lexing rules would have to either:
  • recognise that "<missing>" is a nested string rather than the end of this token followed by a less-than operator (requires keeping track of arbitrarily nested double-quotes); or
  • treat \{ as the end of this token and lex the containing expression normally, but then recognise } as the beginning of the next string token (requires keeping track of arbitrarily nested curlies); or
  • lex the entire string literal as a literal (which would require it to take account of nested quotes and curlies too) and then have the parser reinvoke the lexer to re-lex the contained expression; or
  • the language feature would have to disallow nested string literals and curlies entirely. This option would be annoying to the programmer, especially given that the claim to supporting arbitrary expressions is now no longer true.
This seems not worth it considering that the previous alternative ("My name is "{Name ?? "<default>"}" and...") is only one character longer in code and vastly more straight-forward to lex. There is no need for the lexer to take care of any nesting, and the grammar reduces (<stringliteral> ('{' <expression> '}')*)* <stringliteral>opt to a non-terminal that the compiler transforms into a call to string.Format.
Apr 6, 2014 at 10:20 AM
Edited Apr 6, 2014 at 10:47 AM
My opinion about string interpolation is based on the KISS principle "Keep is simple, stupid". It should not attempt to be full-featured replacement of string.Format, but rather lightweight string.Format alternative with similar syntax for composing strings.

Q. Default culture?
  • CurrentCulture as the default - it won't break the result when user starts refactoring string.Format expressions!
  • There really should be an option to specify the culture. The last proposal with 'using' seems fine to me.
  • Or maybe like this using culture in the last braces
string str = $"Today is: {DateTime.Now}{Culture=CultureInfo.InvariantCulture}";
Q. Prefix syntax
  • definitely with prefix. We don't want all existing strings containing braces to start behaving like interpolated string out of the blue.
  • $ looks good to me, no confusion with verbatim strings, easy to remember
Q. Hole syntax
  • I see no problem with the first "brace" syntax - there is no backslash clutter and the fact the string starts with $ gives user immediate information that braces have special meaning here. It's also very similar to string.Format syntax which is quite useful.
  • Alternatively we could take a look on the Razor syntax, it uses only single character to denote simple expressions, but it is something not usual for developers, that know how string.Format works.
Q. Expression in holes
  • I'd recommend to take inspiration in Path syntax in XAML Binding.
  • The Path property can be either single property, or traversal to nested property, or indexer as well.
Text="{Binding MyModel.Cars[1].IsStarted}"
string str = $"The second car is started: {MyModel.Cars[1].IsStarted}";
  • It's not allowed to use invocations in XAML for good reasons - this could lead to overly complicated solutions like interpolated string as an argument of invocation inside interpolated string, etc. I'm not saying it cannot be done, but it will be quite an overkill that we don't want.
  • In XAML if the expression contains nulls or other Exceptions, it's silently ignored. In string.Format if it's just a simple null value, it's ignored, but any Exception when evaluating more complex expression is thrown. We should probably follow similar behavior to string.Format when evaluating nulls and Exceptions.
  • yes for format specifiers.
Q. Verbatim string
  • I think Interpolated strings should work as a feature superset of verbatim strings, so no string interpolations inside regular verbatim strings.
Question to think about - option to interpolate plain strings?
  • Lets say I have simple string, that I want to interpolate on other place, typical scenario: AppResources. How to do that?
// what about like this
string format = localizedStrings.StartedCarFormat;
// == "The second car is started: {MyModel.Cars[1].IsStarted}"
string localizedString = $format;
  • Should we have the option to use string from resources, that will be later interpolated in the app?
Summary:
  • Interpolated strings should be lightweight alternative of string.Format, based mostly on string.Format syntax, with expressions in holes based on Binding.Path syntax.
Apr 6, 2014 at 4:20 PM
My vote is for using a prefix so existing strings aren't affected, and not allowing it in verbatim strings. Most of the time I use verbatim strings because I'm writing a regex and hate escaping things then re-escaping them. Also for the same reason, existing verbatim strings shouldn't be affected.

Perhaps there's some merit to supporting string str = $@"verbatim interpolated string {x}" but I personally don't see a lot of use cases for it.

I think it should compile to calls of string format, with the expressions replaced with numbers as mentioned. This means converting existing cases of string.Format should be very straightforward.

String interpolation does concern me a bit with the potential developers may write code like the classic PHP:
~~~
$pass = mysql_query("select password from dbo.users where username = '$_GET["username"]' ")
~~~

But the benefits with log messages, and quick messages for non-enterprise applications is pretty big.

Removing the ability to do arbitrary expressions inside the interpolation would mean the above PHP code wouldn't be as easy to write, but it also would make lots of other perfectly valid code difficult to write. If indexers and property accessors are allowed, then function calls should be, so at least the programmer can write $(Escape(Request.Form["username"])) and it'd still scare me a bit, but at least it's a bit safer.
Apr 6, 2014 at 5:16 PM
lwischik wrote:
For the semantics, if you're just calling ToString(), that's exactly what String.Format does!
As I said, string.Format is not an option - InStr can behave similar, but it's separate feature which can "beautify" code and make it less error prone.
  • String.Format works when an argument is null
When argument is null, InStr will just skip it.
Please everyone ensure your posts are polite and contain no inflammatory or aggressive language
Sorry, agree. My emotions so high because I know this feature 'costs' 10 lines of code, while discussion of this feature already took thousand! How it can be so irrational? I think "D way" could be useful here: first, immediately implement feature by some draft. Second, give it to people for real use. Read feedback. Fix issues and improve syntax/logic/etc. Return to step 2. This way we can reach ideal way faster than drawing circles in clouds.
Apr 7, 2014 at 12:37 AM
I actually agree with Vincent3000 on the implement first, discuss later approach.

Now that Roslyn is open source, I'd love to see an experimental version of the language be released every so often with new features implemented the way the core team thinks works best. Then let the community play with it, and see if what they think. If someone uses a feature, and it acts differently then they expected, then discussion should be brought up. At that point, any features that has been settled on can be put into a real release, with any that are still in discussion remaining in the playground.

That being said, now that Roslyn is open source, this doesn't even require the work of the Roslyn team to do so. I don't consider myself experienced enough to really contribute in a huge way, but I'm going to try to learn the code base, and hope that others will join me. We can implement features in forks that we think work, and others can provide feedback. If the community has an implementation, and agrees that it works, then it shouldn't take as much work for the core Roslyn team to go over the implementation, fix any problems they notice, and bring into the core implementation.

Releasing Roslyn as a nuget package was a really smart decision, because it means beta features can be released through there, allowing teams to turn on or off the beta features as required.
Apr 7, 2014 at 5:57 PM
Edited Apr 7, 2014 at 6:03 PM
From the localization point of view, it would be of an advantage to be able to convert a normal string into an interpolated string -- like string.Format does.

Use case: a developer creates a vanilla string in "Developer English":
"Do you really want to delete the file {filename}?"

It gets translated into other languages:
"Sind Sie sicher, dass Sie die Datei {filename} wirklich löschen wollen?"
"Вам совершенно точно больше не нужен файл {filename}? Подумайте."
etc.

Now, the developer has to substitute {filename} with the real variable content in the code. So we need a library method string.Interpolate(string format, object context) or the like, so that the substitution would look like var translated = string.Interpolate(template, new { filename = filename });.

Edit: Oh, I see this problem has been already raised in Necroman's comment.
Apr 7, 2014 at 10:23 PM

Cultures

Being first and foremost intended for building API strings, I would say any culture dependent culture (:)) is out of the question. The best bet is InvariantCulture.

Allowing the specification of the culture would become very cumbersome and would defeat the whole purpose of the feature.

Prefix vs. Hole Syntax

I really hate the { proposed for C# instead of a prefix. I can't see a reason for that.

Why not using # or $ as a prefix for interpolated strings. For C#, this prefix could even be combined with the verbatim string prefix (**(**@**).

Verbatim Strings

Why not?

Really! Of course it should be allowed. Unless there's a very compelling reason for not to.

Expressions In Holes

DevOps is becoming more and more common. This will require many developers to write scripts in scripting languages like Powershell which allows expressions in interpolated strings. Why not allowing it also in C#/VB?

Format Specifiers

Of course they should be allowed.

Constants

Only if the compiler is able to generate compliable code for the constants. That would mean that for some scenarios this feature wouldn't lead to a call to string.Format.
Apr 8, 2014 at 12:10 PM
Really! Of course it should be allowed. Unless there's a very compelling reason for not to.
I'd say allow a combination $@"verbatim {interpolated} string", but do not pollute existing ones, no matter what you do. Verbatim strings are used in regular expressions a lot, and having to escape anything in a verbatim string would be very annoying.
Apr 9, 2014 at 9:42 PM
mirhagk wrote:
We can implement features in forks that we think work, and others can provide feedback. If the community has an implementation, and agrees that it works, then it shouldn't take as much work for the core Roslyn team to go over the implementation, fix any problems they notice, and bring into the core implementation.
I agree with that. However, even in open source world you can't force C# Team to accept a pull request. That's why I think it's good to discuss things first. Project owner might save you a lot of work saying that it's not gonna make it into Main before you start working on particular feature.

Anyway, who's going to implement community-version of string interpolation? :)
Coordinator
Apr 9, 2014 at 9:48 PM
marcinjuraszek wrote:
However, even in open source world you can't force C# Team to accept a pull request. That's why I think it's good to discuss things first.
The way I see it, the job of a prototype is to uncover issues that you hadn't even thought of. In this case, there are a lot of issues to answer first that we're already aware of!
Apr 19, 2014 at 1:22 AM
Hi,

The first thing I thought about was how useful this feature would be when writing exception messages. I'd probably write them more often and more verbosely, if I had this feature, I thought. (Barring of course exceptions that require localization due to their being Framework APIs.) I probably wouldn't use it in many other places though.
public double DivideByDistance(double value, Point point1, Point point2)
{
  Vector vector = point - point2;
  var distance = vector.Length;

  if (distance == 0)
  {
    throw new ArgumentException("The specified value \"\{value}\" cannot be divided by the distance of two equal points, \{point1} and \{point2}.  Division by zero is not permitted.");
  }

  return value / distance;
}
- Dave
Apr 28, 2014 at 12:02 PM
Edited Apr 28, 2014 at 12:06 PM
String Interpolation is no language feature, IMO.

I have a triplet of extension methods covering almost any case mentioned in this post with 26 LOC. First, a [Format]-method taking a delegate, second a IDictionary-variant calling the first using a lambda, third a Object-variant calling the first using a lamba. Also handles conversion of IConvertibles through using CurrentCulture. Very much the same as the "Python-way" explained above.

One of the things I didn't find here was the problem of leading or trailing characters after null-values, resulting in redundant characters.
Dim s = "There is a {building} in {city}, {state}"
'If state is null
Dim r = "There is a house in New Orleans, "
Clearly, the comma and the trailing blank are redundant. I solve this by writing:
Dim s = "There is a {building} in {{city}, {state}}"
This feature-set is based on elementary string-parsing; here are the internals:
Dim s = "There is a {building} in {{city}, {state}}"
Dim r = s.Ranges() 'Returns an IEnumerable<String> as follows: {"There", " ", "is", " ", "a", " ", "{", "building", "}", " ", ....}
Dim i = r.Tokens() 'Returns an IEnumerable<String> as follows: {"There is a ", "{building}", " in ", "{{city}, {state}}"}
Dim t = i.Select(function(x) if x.IsToken then .... else .... end if) 'outer replace; the terminal outer bracket is simply handled as a recursion
Dim a = t.Join(String.Empty) 'Output; Join being the simplest choice
The easiest call is:
Dim s = "There is a {building} in {{city}, {state}}".Tokenize(New With {.building = "house", .city = "New Orleans", .state = "Louisiana"})
Ooops, forgot something; evaluating on-demand values through lambdas is equally possible:
Dim s = "There is a {building} in {{city}, {state}}".Tokenize(New With {.building = "house", .city = "New Orleans", .state = function() GetStateOf(.city)})
A planned addition to add annotations, to enforce uppercase or lowercase has not yet materialized.
May 1, 2014 at 8:03 AM
Here's an idea when designing the String Interpolation language feature - there is already quite similar feature in .NET - the DebuggerDisplay attribute:
http://blogs.msdn.com/b/jaredpar/archive/2011/03/18/debuggerdisplay-attribute-best-practices.aspx
[DebuggerDisplay("Student: {FirstName} {LastName}")]
public sealed class Student {
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
It allows to customize the displayed information when debugging your app. It even support almost any Expressions in curly braces like logical operators, calling functions, etc.
I'm not saying Interpolated strings should be implemented in the exact way, but it's definitely a nice starting point for designing this new language feature.
May 1, 2014 at 4:40 PM
AAARGHH!!!!

DebuggerDisplayAttribute doesn't use \ before the {s for interpolation.
May 3, 2014 at 6:58 PM
Whatever the feature will look like it should probably map 1:1 to string.Format. This keeps the concept count low. We can just tell people: string interpolation is this C# 6 cool thing that makes string.Format easier. Everyone gets that. No junior devs make mistakes due to slight deviations in behavior.

Same semantics (except of course the different hole syntax). Culture support. Support for custom format strings.

The C# compiler should of course optimize the computation because many common cases will map to a very fast call to string.Concat.

That said, how many places does a typical code-base have that require string formatting?! This feature is a candidate to be dropped. Please use your time for something else!


One additional idea that stems from two use cases:
  1. I want to specify a culture
  2. I want to HTML-encode holes
Let's give the programmer a way to specify a custom formatter. I could say:
$"hello {name:System.Globalization.InvariantCultureFormatter}!"
and this would statically bind to the expression System.Globalization.InvariantCultureFormatter.ConvertToString(name). Or:
$"hello {name:System.Web.HtmlEncodeFormatter}!"
It would be useful to specify the formatter for the whole string, of course:
$"hello {name}!" using HtmlEncodeFormatter
Making outputting HTML trivial. Imagine the alternative:
"hello " + HttpUtility.HtmlEncode(name) + "!"
This is much worse.

In these examples I have assumed that the formatter types provide a static method that would convert anything (subject to normal method overloading rules!) to a string. This is an idea that I want to contribute because previously I have only seen examples where someone passed in an instance of IFormatProvider. By using static methods and overload resolution we could increase performance.

Note, that with "static using" specifying a culture as an object reference would not be too bad either:
using CultureInfo;
//...
$"hello {name:CurrentCulture}!"
Resharper would surely offer help adding that using statement. This would be very low friction.



So what's the default culture/formatter. I'd like the feature to have none. It must be specified. This is to enforce code quality in a junior team.

On the other hand this might be an impractical barrier of entry. If you must have a default formatter use the same that string.Format uses: CurrentCulture. As horrible as this is I think it's the right choice.
May 3, 2014 at 9:15 PM
xor88 wrote:
That said, how many places does a typical code-base have that require string formatting?! This feature is a candidate to be dropped. Please use your time for something else!
I can see many uses for String.Format and StringBuilder.AppendFormat overloads which provide the ability to access a member of a passed-in object as part of processing; from a format-specification standpoint, something like {3.X:0.00} would access member X from a argument 0 and format it as "0.00"[the behavior would probably be compatible withString.Format`, but adding the ability to invoke arbitrary members of the passed-in arguments to existing usages might pose a security hole]. I'm not quite clear I see as many uses for having a feature which is in some regards more versatile, but would be limited to literal format strings.
May 3, 2014 at 9:33 PM
davedev wrote:
throw new ArgumentException("The specified value \"\{value}\" cannot be divided by the distance of two equal points, \{point1} and \{point2}.  Division by zero is not permitted.");
How much better is that, really, than
throw new ArgumentException("The specified value {0} cannot be divided by the distance of two equal points, {1} and {2}.  "
           "Division by zero is not permitted.".FormatWith(value, point1, point2) );
How will each approach react if code is reformatted to rename point1 to leftPoint?
May 3, 2014 at 10:28 PM
supercat wrote:
I can see many uses for String.Format and StringBuilder.AppendFormat overloads which provide the ability to access a member of a passed-in object as part of processing; from a format-specification standpoint, something like {3.X:0.00}
That would be nice with short circuiting semantings as in most data binding thechnologies. But that would be something for the BCL, not the language.
May 3, 2014 at 10:29 PM
supercat wrote:
davedev wrote:
throw new ArgumentException("The specified value \"\{value}\" cannot be divided by the distance of two equal points, \{point1} and \{point2}.  Division by zero is not permitted.");
How much better is that, really, than
throw new ArgumentException("The specified value {0} cannot be divided by the distance of two equal points, {1} and {2}.  "
           "Division by zero is not permitted.".FormatWith(value, point1, point2) );
How will each approach react if code is reformatted to rename point1 to leftPoint?
Easier to read and understand, specially when the text is long and/or there are lots of arguments.
May 4, 2014 at 1:57 PM
Edited May 4, 2014 at 1:58 PM
I just realized that we already have a way to add culture and encoding to string interpolation. We can call methods as part of the string:
var temp = 0.0;
"Temperature: \{Format(temp, "F2", CurrentCulture)}"
This would require somehow bringing a "Format" method into scope as well as CurrentCulture. R# could add the static usings automatically. Would also work for HTML:
var name = "x";
"Hello, <b>\{HtmlEncode(name)}</b>"
And we could have a tool (R#) check that all holes in the entire solution are properly encoded/formatted. This way we can force compliance in teams and not have encoding bugs.

We don't even need a default culture now. Just call ToString and whatever that one picks is used. Or better, use the same rules that string.Format uses (which also results in CurrentCulture).
May 10, 2014 at 1:01 AM
Great discussion !!

I vote up for a new string marker for some kind of Razor string, for example something like:
var msg = «Hi @name you have won @creditsWon credits»
It is clear and backward compatible, don't broke anything, in fact going further you can use a prefix to denote that the string contains html inside (with @prefix) , for example:
var msg = @«<div style="margin:10px;">
                      Hi @name you have won <b>@creditsWon</b> credits
                    </div>»
Visual Studio and Resharper can provide full intelisense, html validation, completion and of course compilation support

That syntax is very simple, reuses the Razor knowledge and support of the tooling chain, and with a new string marker you can use inside " and ' without escape them

You can use inside @foreach @if and most of the Razor options

The unique counterpart is that the keyboard don't contain the simbols but I don't see it as stopper because it solve ton of other things :)

Here is the user voice suggestion

http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/5903196-add-new-string-literal-that-contains-razor-code-f

Vote it up if you like the syntax and idea of full Razor string
May 12, 2014 at 1:33 AM
MarcosMeli wrote:
The unique counterpart is that the keyboard don't contain the simbols but I don't see it as stopper because it solve ton of other things :)
Your keyboard doesn't contain the symbols. Mine does.

But that's one of the reasons I prefer a prefix to the string.
May 12, 2014 at 1:53 PM
PauloMorgado wrote:
Your keyboard doesn't contain the symbols. Mine does.
I wish I had a keyboard layout where all ASCII characters could be typed as single characters without dead keys, but diacritics and other special characters could be typed sensibly without silly alt+Number codes. Know of any?
May 12, 2014 at 4:10 PM
supercat wrote:
I wish I had a keyboard layout where all ASCII characters could be typed as single characters without dead keys, but diacritics and other special characters could be typed sensibly without silly alt+Number codes. Know of any?
Fancy to create it? http://msdn.microsoft.com/en-us/goglobal/bb964665.aspx
May 12, 2014 at 6:27 PM
One scenario I think is important is to load (localized) strings from a resource or database and replace named parameters. Probably with some API like string.Format but taking a dictionary instead of an object[]. So besides binding from a string to identifiers in the current scope of a string constant, the same solution should also be able to bind to some sort of dictionary with a simple syntax.

I really like the Razor syntax, it is existing technology and already supports full expressions with real simple syntax etc. (I voted!). Would be great if Razor could be used inside C# like C# can be used inside Razor. (Up to your co-worker to find out how many levels deep this will go). Currently Razor makes it hard (if not impossible) to create top-level variables that are not statically typed, which makes it impractical for the scenario I described above. The best way I found was providing a single static property of type dynamic so the template can do something like
"Hello @Fields.FirstName" (Seems like I just found a use case for the light-weight dynamics that just got dropped :-( )
May 12, 2014 at 6:37 PM
Well if the key doesn't exist on your stock US keyboard I don't think that you're likely to see it included. :)

My other concern here is are you advocating for the integration of the Razor engine within the language? If so to me that would mean that a general-purpose language feature would be based on something designed specifically to work with markup, taking dependencies on various libraries that aren't even stock BCL let alone default for the language. How would this behave for strings that aren't intended to be web views? This would also absolutely require some kind of prefix mechanism as embedding that syntax into a string is completely legit now and we wouldn't want the compiler misinterpreting existing code. Lastly, the scenario in which you would be using this seems to violate the tenets of MVC and how Razor is currently employed by encouraging developers to construct the view (or portions of the view) within the controller.

If that's not the case and you're just looking for a more featureful syntax for string interpolation that feels like Razor is exactly that it would feel like Razor. It wouldn't fit in with the current string formatting syntax used throughout the BCL, and worse developers might confuse it for the fully functional Razor engine which adds extra logic like HTML encoding and partial templates to the raw values being interpolated into the string. That could result in poorly interpolated partial views that contain accidentally dangerous chunks of HTML or script. That and I'm not sure that looping within an interpolation is really a common use-case.

And that last point is something that scares me about interpolation in general. Where it seems to most apply breaks some best practice for the use of strings within an application, whether that be mixing of concerns between business logic and the view, embedding non-i18n friendly strings in the view or exception messages or dynamically constructing unsafe SQL. Granted, I understand most applications don't need to worry about i18n and that is an absolutely valid point.

If we are to have interpolation, I think it should be prefixed and the holes non-escaped. The holes should follow a similar syntax to String.Format except that the index would be replaced by the name of the value to be interpolated optionally with the same width/justification specifiers and format specifier as allowed in composite formatting. The CultureInfo would always default to the current culture, as it does with String.Format, because frankly if you're concerned about i18n/l10n then you're doing it wrong anyway. And the compiler should expand that to nothing more than a simple call to string.Format. As for whether the expressions can be more than simple values that are in scope, I'm ambivalent, but I imagine that allowing arbitrary expressions would increase compiler complexity.
string name = "Bill Gates";
DateTime dob = new DateTime(1955, 10, 28);
string interpolated = $"Hello {name}, you were born on {dob,20:d}, which was a {dob:dddd}.";
May 12, 2014 at 6:58 PM
One common scenario for string interpolation if for generating text in some other language, like SQL, HTML, regex or xPath. This has become a sort of anti-pattern, mainly because developers forget to properly escape what should be literals in the target language, causing things like SQL-injection, cross site scripting or just unexpected behavior if there is a special character in a substituted value.

It would be nice if the interpolation engine would somehow be configurable with an applicable handler that knows how to escape literals in this specific target environment. Kind of like how Rasor by default will html escape any string before sending it to the output. This handler will get a chance to inspect and or update any value before it is pasted into the target string.
May 12, 2014 at 7:04 PM
Edited May 12, 2014 at 7:06 PM
Halo_Four wrote:
Well if the key doesn't exist on your stock US keyboard I don't think that you're likely to see it included. :)
Of course I just point out that we need a new string marker that don't broke previous code and don't use " mostly because a ton of html and other strings contains " inside and we must use escape chars that make the final code hard to read, I saw in the Build 2014 a demo of Roslyn with a new string marker with a dual ^ that I can't even found on charmap, so is not so crazy to think in a new marker
My other concern here is are you advocating for the integration of the Razor engine within the language? If so to me that would mean that a general-purpose language feature would be based on something designed specifically to work with markup, taking dependencies on various libraries that aren't even stock BCL let alone default for the language. How would this behave for strings that aren't intended to be web views? This would also absolutely require some kind of prefix mechanism as embedding that syntax into a string is completely legit now and we wouldn't want the compiler misinterpreting existing code. Lastly, the scenario in which you would be using this seems to violate the tenets of MVC and how Razor is currently employed by encouraging developers to construct the view (or portions of the view) within the controller.
I just suggest a Razor Syntax with some features but it is a compile time process, that Roslyn compiler will convert the interpolation to plain C# code, it must be simple enough. No dll dependencies at all, and Razor is not just for markup it can be use in plain interpolation with a great flexibility and simplicity allowing not only point to variables also using @if @foreach and accessing methods and properties in a know way
If that's not the case and you're just looking for a more featureful syntax for string interpolation that feels like Razor is exactly that it would feel like Razor. It wouldn't fit in with the current string formatting syntax used throughout the BCL, and worse developers might confuse it for the fully functional Razor engine which adds extra logic like HTML encoding and partial templates to the raw values being interpolated into the string. That could result in poorly interpolated partial views that contain accidentally dangerous chunks of HTML or script. That and I'm not sure that looping within an interpolation is really a common use-case.
I talk about use simple Razor in first place and later I suggest to use an HTML marker to allow resharper and visual studio to use intellisense inside, if no additional marker a lot of tooling must be disabled

I don't agree about interpolation as a bad practice because allowing Razor strings is not a bad practice, only if you use it wrong, in fact you can always use plain strings wrong anyway

Best Regards
May 12, 2014 at 7:08 PM
Edited May 13, 2014 at 12:29 AM
If we are to have interpolation, I think it should be prefixed and the holes non-escaped.
How about $"Hello "(name)", you were born on "(dob,"20:d")", which is a "(dob,"dddd")$"? Have the things being evaluated be outside quotes so that Intellisense and refactoring tools would be able to recognize them, and have the above either expand into a string and Object[] if such expansion would be legitimate, or into a call to String.Format with an appropriate format string and Object[]. That would allow efficient use with methods like AppendFormat.

I don't like to have language features overly-strongly tied to a particular method. Something like Delegate.BeginInvoke includes what would be the widely-useful type-safe ability to encapsulate a delegate and its parameters into a single object, but only allows that ability to be used with one particular threadpool. If BeginInvoke had instead been replaced with a method Bind that returned type MethodInvoker, code that wanted to start a thread on the default thread pool could have used System.Threading.DefaultPool.Start(someDelegate.Bind(3,4);)--a little more verbose but also more specific than someDelegate.BeginInvoke(3,4), but something like myControl.BeginInvoke(someMethod.Bind(3,4)) would have been type-safe, unlike myControl.BeginInvoke(someMethod,3,4).

I see a similar situation applying here with String.Format. I can see uses for a language feature which would facilitate preparing a format string and argument list in a fashion that would be suitable for use with String.Format (each parenthesized expression would insert a {0}, {1}, etc. tag within the string, and add a parameter to the array), but I see no reason it should need to directly call that method.
May 12, 2014 at 7:19 PM
FrankBakker wrote:
One common scenario for string interpolation if for generating text in some other language, like SQL, HTML, regex or xPath. This has become a sort of anti-pattern, mainly because developers forget to properly escape what should be literals in the target language, causing things like SQL-injection, cross site scripting or just unexpected behavior if there is a special character in a substituted value.

It would be nice if the interpolation engine would somehow be configurable with an applicable handler that knows how to escape literals in this specific target environment. Kind of like how Rasor by default will html escape any string before sending it to the output. This handler will get a chance to inspect and or update any value before it is pasted into the target string.
I agree. What concerns me is that this becomes a game of whack-a-mole of the language and tidbits of framework both encouraging the user to adhere to the anti-patterns while attempting to prevent them from getting burned. History shows that it's only a matter of time. And PHP is the last language that C# should ever be attempting to emulate.
May 12, 2014 at 7:52 PM
Oof, someone needs to terminate their code block delimiters. :)

I agree, we'd want the IDE support and Intellisense. I can't imagine that the effort in supporting the syntax would be that different if they went either way. Resharper is going to have to modify their parser in order to pick apart the interpolation syntax. Any project using Roslyn's APIs would get that for free.
May 12, 2014 at 10:14 PM
Halo_Four wrote:
If we are to have interpolation, I think it should be prefixed and the holes non-escaped. The holes should follow a similar syntax to String.Format except that the index would be replaced by the name of the value to be interpolated optionally with the same width/justification specifiers and format specifier as allowed in composite formatting. The CultureInfo would always default to the current culture, as it does with String.Format, because frankly if you're concerned about i18n/l10n then you're doing it wrong anyway. And the compiler should expand that to nothing more than a simple call to string.Format. As for whether the expressions can be more than simple values that are in scope, I'm ambivalent, but I imagine that allowing arbitrary expressions would increase compiler complexity.
I agree with you , but I'm afraid the team is very set up on escaped holes instead of prefixed strings.
string name = "Bill Gates";
DateTime dob = new DateTime(1955, 10, 28);
string interpolated = $"Hello {name}, you were born on {dob,20:d}, which was a {dob:dddd}.";
The problem with this is, what culture is this using?
May 12, 2014 at 10:18 PM
MarcosMeli wrote:
I just suggest a Razor Syntax with some features but it is a compile time process, that Roslyn compiler will convert the interpolation to plain C# code, it must be simple enough. No dll dependencies at all, and Razor is not just for markup it can be use in plain interpolation with a great flexibility and simplicity allowing not only point to variables also using @if @foreach and accessing methods and properties in a know way
Powershel doesn't need that and has true string interpolation. A lot easier to understand and master than Razor.
May 12, 2014 at 11:04 PM
MarcosMeli wrote:
I don't agree about interpolation as a bad practice because allowing Razor strings is not a bad practice, only if you use it wrong, in fact you can always use plain strings wrong anyway
The normal use of Razor is specifically within the view of an MVC application, which is the proper place for it. While you can argue that at that point it is more or less like string interpolation on steroids that is way oversimplifying the purpose. If you start to move Razor into strings within the MVC controller I would argue that you're starting to mix your concerns and that is inappropriate.

I'm actually finding it very difficult to think of a valid use of interpolation that doesn't mix concerns (view generation in business logic), imply some form of special encoding (SQL, HTML, URI) or that doesn't have some technically more appropriate solution (exception messages, snippets of view text).
May 12, 2014 at 11:16 PM
PauloMorgado wrote:
Halo_Four wrote:
If we are to have interpolation, I think it should be prefixed and the holes non-escaped. The holes should follow a similar syntax to String.Format except that the index would be replaced by the name of the value to be interpolated optionally with the same width/justification specifiers and format specifier as allowed in composite formatting. The CultureInfo would always default to the current culture, as it does with String.Format, because frankly if you're concerned about i18n/l10n then you're doing it wrong anyway. And the compiler should expand that to nothing more than a simple call to string.Format. As for whether the expressions can be more than simple values that are in scope, I'm ambivalent, but I imagine that allowing arbitrary expressions would increase compiler complexity.
I agree with you , but I'm afraid the team is very set up on escaped holes instead of prefixed strings.
Yeah, and that's fine. It's probably more C#ish than a prefix, particularly $. Not my preference, but it's not like this is a democracy.
string name = "Bill Gates";
DateTime dob = new DateTime(1955, 10, 28);
string interpolated = $"Hello {name}, you were born on {dob,20:d}, which was a {dob:dddd}.";
The problem with this is, what culture is this using?
None. It would translate literally:
String.Format("Hello {0}, you were born on {1,20:d}, which was a {1:dddd}.", name, dob);
Internally that calls StringBuilder.AppendFormat without an IFormatProvider which basically leaves it up to the arguments themselves, although it seems that the normal behavior is to then use the current culture of the current thread.

As I mentioned, if one was so worried about the culture in play, why would they be using interpolation with a string literal with a hard-coded language? I guess the argument is that the hard-coded English statement might have a formatted French date, but at that point we've already entered a very muddy world. Maybe if it always passes CultureInfo.InvariantCulture instead? But then you'll have hard-coded French statements with formatted invariant (which is basically English) dates. Lose-lose, and an anti-pattern for i18n concerns.
May 13, 2014 at 12:01 AM
Halo_Four wrote:
As I mentioned, if one was so worried about the culture in play, why would they be using interpolation with a string literal with a hard-coded language? I guess the argument is that the hard-coded English statement might have a formatted French date, but at that point we've already entered a very muddy world. Maybe if it always passes CultureInfo.InvariantCulture instead? But then you'll have hard-coded French statements with formatted invariant (which is basically English) dates. Lose-lose, and an anti-pattern for i18n concerns.
That was your exemple. And, for me, the output was:
Hello Paulo, you were born on 1967-07-18, which was a terça-feira.
May 13, 2014 at 12:15 AM
PauloMorgado wrote:
Halo_Four wrote:
As I mentioned, if one was so worried about the culture in play, why would they be using interpolation with a string literal with a hard-coded language? I guess the argument is that the hard-coded English statement might have a formatted French date, but at that point we've already entered a very muddy world. Maybe if it always passes CultureInfo.InvariantCulture instead? But then you'll have hard-coded French statements with formatted invariant (which is basically English) dates. Lose-lose, and an anti-pattern for i18n concerns.
That was your exemple. And, for me, the output was:
Hello Paulo, you were born on 1967-07-18, which was a terça-feira.
Yep, which is why I'm making the argument against mixing i18n and interpolation. :)

For those business line apps for which there is no hope or care of i18n then the hardcoded strings and current thread culture would make sense, moreso than invariant culture would. Then you can hardcode your Portuguese strings from hoje to terça-feira. :)
May 19, 2014 at 10:16 AM
Will this mean that the string literal is parsed and and available for analysis via Roslyn api? ie
StringFormatArg::= "{" Index ("," alignment)? (":" format)? “}“
Whilst correctly identifying, escaped opening and closed braces?

This would mean that it isn't just a string literal, it content has a contextual meaning to the surround expression.

Which could be a powerful feature to have at compile-time. Check that args in the string are they valid? Within range? Is there enough args suppliied along with the formatstring?
String interpolation with named indices,, would just be an addition indexpart parser.
You could go to the exreme and implemement a parse that does arbitrary expressions. Like Nemerle



Then you could classify them as a subclass.
FormatStringLiteral <: StringLiteral
Also attribute possibke consumers with additional metadata that the string should be formatstring
May 21, 2014 at 7:37 AM
My vote is against interpolation: it ties the string to the details of the variable names, which is almost always the wrong thing to do from a localizability perspective.

Likewise embedded expressions.

However, the Python option is friendly to localizable resources, and avoids positional argument hell. Just create a new format method that takes a dictionary to apply to a string that uses named arguments.
May 21, 2014 at 8:10 AM
Edited May 21, 2014 at 10:08 AM
my opinion on string interpolation;

Create a new struct of with a type name of something line InterplotatedString which looks like;
public struct InterpolatedString
{
    private string format;
    private object[] args;

    public InterpolatedString(string format, params object[] args)
    {
        this.format = format;
        this.args = args;
    }

    public string Format
    {
        get { return format; }
    }

    public object[] Args
    {
        get { return args; }
    }

    public override string ToString()
    {
        return string.Format(format, args);
    }

    public string ToString(IFormatProvider provider)
    {
        return string.Format(provider, format, args);
    }

    public static implicit operator string (InterpolatedString interpolatedString)
    {
        return interpolatedString.ToString();
    }
}
then when using string interpolation instead of directly mapping to a call to string.Format then you instead just wrap to a new instance of InterpolatedString
this would give us the the benefit of providing programmatic access to the values for those situation where an api doesn't internally use string.format or does some other manipulation first while also having the implicit cast allow other developers to use it as a native string without thought. Additionally due to the overload of ToString() it would also support passing a format provider for culture support (if required).

So an end to end translation could be something like this
var t = $"Joe Blogs was born on { joe.Dob : dd-mmm-yy } this was { DateTime.Now.Subtract(joe.Dob).TotalDays } days ago";
with joe being a simple POCO with a Dob property exposing a DateTime
this would translate to
var t = new InterpolatedString("Joe Blogs was born on {0:dd-mmm-yy} this was {1} days ago", joe.Dob, (DateTime.Now.Subtract(joe.Dob).TotalDays));
May 21, 2014 at 8:57 AM
kfarmer wrote:
My vote is against interpolation: it ties the string to the details of the variable names, which is almost always the wrong thing to do from a localizability perspective.
It's already tied through calls to string.Concatenate or string.Format. The compiler is just not helping you on that now.
Likewise embedded expressions.
The same.
However, the Python option is friendly to localizable resources, and avoids positional argument hell. Just create a new format method that takes a dictionary to apply to a string that uses named arguments.
Creating a dictionary would involve unnecessary heap allocations.
May 21, 2014 at 9:36 AM
Edited May 21, 2014 at 7:28 PM
Have at look at the project String Format Diagnostic

Which provides validation of the text formatstring pre compilation, via roslyn code diagnostic.

I could imagine a fork that modifies the part of the parser that gets the arg indexbeibg replaced the identify identifiers.
Then check that those are accessible.
Jun 2, 2014 at 11:18 PM
I don't personally see the need for this over String.Format, and I strongly agree with kfarmer's point about mixing code elements into string literals, but what's really bugging me is calling it "interpolation." It's probably minor compared to the go/no-go question, but I think it's a misuse of the word. It certainly doesn't match the math/engineering uses I'm aware of (simply put, deriving intermediate data points from a known range).
Jun 3, 2014 at 6:46 PM
MV10 wrote:
I don't personally see the need for this over String.Format, and I strongly agree with kfarmer's point about mixing code elements into string literals, but what's really bugging me is calling it "interpolation." It's probably minor compared to the go/no-go question, but I think it's a misuse of the word. It certainly doesn't match the math/engineering uses I'm aware of (simply put, deriving intermediate data points from a known range).
Can't really say when in the history of programming languages that the term "interpolation" took on the meaning of embedding variables in strings but it's certainly not new to C#. If Microsoft went with any other name it would probably be more confusing to the community as a whole.

Have you looked at the recent announcement of Apple's Swift language? It has interpolation as a feature with the same name and a very similar syntax to the original proposal in Roslyn: let s = 'Hello \(name)'
Jun 3, 2014 at 6:49 PM
Huh. I've been programming more than 30 years and somehow missed that usage completely. Thanks.
Jun 4, 2014 at 12:27 PM
Hey, my "slow mo friends", STILL DISCUSSING FEATURE???? Look around, VS 2014 CTP is out! WITHOUT interpolation string! WTF is going on, dudes? Was it so hard to implement interpolating strings SOMEHOW? Who care so much how it will look - $name, {name} or wh@teversh*t{name} - IT IS NOT DONE.

Thanks everybody for wasting time! Thanks for all your useless comments, esp. "xor88"! Now we nave NOTHING.

What else you wanna discuss? May be china characters? Or angle of "angle brackets"? C'mon, don't feel shy - put more rubbish in discussion, it's so productive to listen your comparison of GODDAMN SIMPLE INTERPOLATION with your vision of string.Format!

I need just one answer: who is responsible for "interpolating strings" and why that @%#$@ still didn't do it?
Jun 4, 2014 at 12:40 PM
I vote for Vincent3000 to be put in charge of the C# language. Finally someone who gets it. Just put all the useful stuff in there already! :)
Coordinator
Jun 4, 2014 at 2:18 PM
xor88 wrote:
I vote for Vincent3000 to be put in charge of the C# language. Finally someone who gets it. Just put all the useful stuff in there already! :)
Seconded. We appreciate the offer, Vincent3000. Code up the change, submit a pull request, and we'll review it! :)

But seriously, Vincent, the discussion here has not played ANY role in delaying the feature. (On the contrary, I suspect we the language team would have simply dropped the feature already without the evidence of demand for it, and without evidence that the community is happy with the design).

As for the implementation? You can see there's a long list of proposed language features, and we're now working through them gradually, making sure that we get each one in a state we're proud of before moving on to the next.
Jun 4, 2014 at 7:28 PM
Edited Jun 4, 2014 at 7:36 PM
Similar in concept to tocsoft's but with tweaks
What about these basic class layouts for an implementation.
Class InterpolatedString
  Inherits StringLiteral  '<-- It's still a string literal 
  .Args  : List<Arg>

Class Arg
  .Span      : TextSpan
  .IndexArg  : ArgIndexBase
  .Alignment : AlignmentPart
  .Format    : FormatPart

Base Arg_Base
  .Span      : TextSpan
Base ArgIndexBase
  Inherit Arg_Base

Class ArgIndex_Indexed
  Inherits ArgIndexBase
  .Index : Integer

Class ArgIndex_Identifier
  Inherits ArgIndexBase
  .Identifer : Identifer

Class AlignmentPart
  Inherit Arg_Base
  .Alignment : Integer

Class FormatPart
  Inherit Arg_Base
  .Format : String
Which allows the compiler / coder additional access to the content of the interpolated string.
For example renaming of the variable from joe to alice would auto-magically rename the content of the interpolation string. since the arg is accessible and contextually meaningful to the compiler. As well as other code diagnostics.

The Editor could that apply additional formatting to the text.
Eg
var t = $"Joe Blogs was born on { joe.Dob : dd-mmm-yy } this was { Days } days ago";

If you want to extend to include expressions inside of the 'holes'
Class ArgIndex_Expression
  Inherits ArgIndexBase
  .Expression  :  Expr
Jun 5, 2014 at 12:25 PM
lwischik wrote:
We appreciate the offer, Vincent3000. Code up the change, submit a pull request, and we'll review it! :)
Hey, don't you think I didn't tried?? I did! I was so tired waiting for so simple thing that started feature myself. I implemented "interpolating strings" as string with "christmas tree" quotes («») till stage "source is parsed by compiler and some internal exception happen". Unfortunately I have no access to documentation (if it exists at all) and couldn't "push thru" my "new type string" to AST. But after some discussion I realised that even existing strings can easy adopt new "dollar syntax":
var s = "My name is $name" - this is more than enough for all our needs! No function calls, expressions, etc. Just a simple variable or field/property. Period.

From my view, better to have AT LEAST SOMETHING than nothing at all! Look at D language - they try, fall, stand up and try again! And their features probed in real life, after what feature grows according to real needs, not according to "vision of C# team". Feel the difference? That's why I asked: "make it any simplest way, doesn't matter" - because (like "conditional access operator") we always can EXCLUDE feature from release! But now we wanna test - how life is good with interpolating strings?
...we... dropped the feature already without the evidence of demand for it
There IS demand, otherwise we didn't discuss it here, right? :) Just think why it's implemented in Perl, Ruby (may be even Python): because IT IS HANDY! And less error prone. Who wants to deal with all that string.Format positions counting, when we can just write $var?
As for the implementation? You can see there's a long list of proposed language features, and we're now working through them gradually, making sure that we get each one in a state we're proud of before moving on to the next.
I agree, there is A LOT of nice things we would like to have in C#, but don't you have some "professional intuition" related things? For me "static using" is just a "mess producer": when you write method, all functions you call (without object".") is not a "local methods" anymore - it can be any method, imported from 2-5 another classes - GODDAMN, WHY??? Don't you see it's stupid? Or "primary constructor" - same thing, where I'l never waste my time. That's why you have limited time - because wasting it on definitely wrong ideas. And believe me - I could be happy to be in "C# team" to push things forward! GOOD things forward. :))

Back to "int.strings": I wish to have it as simple as this: var s = "My name is $name"; Period. No bells and whistles, no coffee maker, no nose trimmer, just "take variable/field/property value and insert into string". Sure any guy in "C# team" can do it in 4 hours, but hell... things move so slo-o-o-ow!
Jun 5, 2014 at 2:30 PM
Vincent3000 wrote:
Back to "int.strings": I wish to have it as simple as this: var s = "My name is $name"; Period. No bells and whistles, no coffee maker, no nose trimmer, just "take variable/field/property value and insert into string".
And you break a ton of existing code as a result. Every tiny change has consequences and anything that makes it into release becomes a permanent part of the language. I applaud the C# team for taking their time and thinking it through while keeping an eye on the next release
Jun 5, 2014 at 11:37 PM
@Vincent3000, we couldn't be further apart on this. So far apart that I (that am know to have outbursts like yours but tend not to in such a public forum) will try to give you a polite answer.
Just think why it's implemented in Perl, Ruby (may be even Python)
In case you didn't know, Powershell has a powerful string interpolation feature. Seems to be a common feature for SCRIPTING languages.

So, if this is the most important thing in the world for you, more important than other easier to implement and time saver features, maybe you should keep scripting and leave programming for the grownups.

I can see so many ways that this feature can go wrong that I'm glad it's not in the CTP. But then again I'm not as brilliant smart and know it all like you.

Please, keep telling us what to think and what to do. I'd hate to think that I'm thinking or doing something stupid (like writing this reply).
Jun 6, 2014 at 12:04 PM
PauloMorgado wrote:
In case you didn't know, Powershell has a powerful string interpolation feature. Seems to be a common feature for SCRIPTING languages.

So, if this is the most important thing in the world for you, more important than other easier to implement and time saver features, maybe you should keep scripting and leave programming for the grownups.
You seem to be implying that scripting languages are not serious, professional languages. That's wrong.
To me, being a "scripting language" simply means having the quality of being good for interactive use (things like dynamic typing and interpreted/compiled are irrelevant).
Definitely not C#'s strong point right now, but improvements can be gradual.
Jun 6, 2014 at 6:21 PM
eldritchconundrum wrote:
PauloMorgado wrote:
In case you didn't know, Powershell has a powerful string interpolation feature. Seems to be a common feature for SCRIPTING languages.

So, if this is the most important thing in the world for you, more important than other easier to implement and time saver features, maybe you should keep scripting and leave programming for the grownups.
You seem to be implying that scripting languages are not serious, professional languages. That's wrong.
To me, being a "scripting language" simply means having the quality of being good for interactive use (things like dynamic typing and interpreted/compiled are irrelevant).
Definitely not C#'s strong point right now, but improvements can be gradual.
Although I intended to seem like I was diminishing scripting languages, I did not intend to effectively do so.

These days I find myself doing a lot of things in Powershell that, in the past, I would have built my own tools.

Extreme careful must be put in changes for them to become improvements.
Jul 18, 2014 at 12:10 AM
PauloMorgado wrote:
Just think why it's implemented in Perl, Ruby (may be even Python)
In case you didn't know, Powershell has a powerful string interpolation feature. Seems to be a common feature for SCRIPTING languages.
By your ridiculous logic if cart (with horse) has round wheels, diesel cars should have square wheels (because round wheels is so "horsy"!).
Man, leave programming, because you have no logic at all.
I discuss interpolating string HERE because I need it in C#! Why the hell you advice me scripting??
And again I see no any progress: people is busy with ridiculous "primary constructor" (which will be used in 0.000001% cases) and do nothing for interpolating strings - what's going on, dudes?
One mistake you do (thinking about yourself as "good" architector) is that every feature you wanna implement should be "The Earth turning tool". Relax, guys, get back to the office! You're not Einstein and we ask you not the space shuttle! Just implement it as simple as possible, ONLY PRACTICE (not your "thinking") can show how you can/could do it. Without real usage you definitely will make mistake in designing feature - because you're just humans and only some_of_you have more-less good capabilities to do "designing" job. Trust community, leave ambitions. :)
Jul 18, 2014 at 6:50 AM
Edited Jul 18, 2014 at 6:51 AM
Vincent3000 wrote:
One mistake you do (thinking about yourself as "good" architector) is that every feature you wanna implement should be "The Earth turning tool". Relax, guys, get back to the office! You're not Einstein and we ask you not the space shuttle! Just implement it as simple as possible, ONLY PRACTICE (not your "thinking") can show how you can/could do it. Without real usage you definitely will make mistake in designing feature - because you're just humans and only some_of_you have more-less good capabilities to do "designing" job. Trust community, leave ambitions. :)
The way I see it is you want them to implement this because you subjectively prioritized it as the number one feature that everyone needs but in fact, while I want it as much as you do I think that you can speak for yourself and not in the name of everyone else, not to mention, that I think you can tone down the language and behave more professionally because you are a professional, right?

Yes Vincent, it definitely shows in your posts how much you know about design, we have a process called design because we are humans and because we do make mistakes, otherwise, we wouldn't have it at all and every decision was right on the spot.

I'd say listen to the community but don't trust their judgment, that's a decision too and it's probably the correct bet, do the math. ;)
Jul 18, 2014 at 12:43 PM
Edited Jul 18, 2014 at 12:59 PM
I think the key thing is that the first iteration of it must be right, or near enough right. Even though here should be considered experimental, there will always be people that think that's not true, and will expect features not to be dropped. That unfortunately means that we need to discuss what the best option is without having lots of real world data.

The beautiful thing is that Roslyn is 100% open source, and the source is pretty well architected at that. If you desperately want a feature, and think you're better at doing stuff than the maintainers of the project, you can go ahead and fork it yourself.
Jul 18, 2014 at 2:48 PM
Why don't you implement the feature yourself? If the implementation is good enough maybe it'll get pulled into the C# compiler official source code.
Jul 23, 2014 at 7:09 PM
OK, so um, I think we jumped off the tracks somewhere.

My vote would be with the $"2 + 2 = {2 + 2}" syntax, allowing arbitrary code in the hole. The only big concern I have with it is the culture. Part of me says it should simply always be InvariantCulture, since any localized strings will probably be stored in resource files, which won't be able to use this syntax. However that would be a pain for the naive people like me who only target a single culture. I don't think we would ever really need to use a culture other than Invariant and the current thread culture, so why not have one more type of string literal to make the distinction? $"..." for InvariantCulture, and $$"..." or #"..." for the current culture. (Or vice versa, actually.) On the edge case where you need to use a different culture, fall back to Format(), or temporarily change the current culture.

I wonder how VS would colorize this...
Jul 23, 2014 at 11:52 PM
@Yota Can I also put Interpoled String inside of the arbitrary code hole? also with arbitrary code?
Jul 24, 2014 at 12:29 AM
AdamSpeight2008 wrote:
@Yota Can I also put Interpoled String inside of the arbitrary code hole? also with arbitrary code?
You would have to be able to since such a string counts as arbitrary code, which is what makes me wonder about the colorization. Perhaps slightly darkening the text or background for each level of depth.
Jul 24, 2014 at 1:04 PM
@Yota it looks like they've already nailed down the design for now, although it can of course be extended in the future.


For now they are only allowing simple variables inside, no expressions. It'll basically just be syntatic sugar for String.Format. In the future they want to possibly allow:
LOC$"Hello, {name}, you have {amount} donut\{s\} left."
SQL$"…"
URI$"…"
Which would pass it to a function that could do escaping etc. I'll be looking forward to this. It could also handle culture and just assume the same default as the rest of .NET (current culture).
Jul 26, 2014 at 2:41 AM
I"d like to make some points in the context of a common use I see of string.Format, a situation where the developer doesn't need its formatting capability, but simply to make the string more readable than concatenation:
string.Format("My name is {0} and I am this many {1}", firstName, age); //not using any formatting, but much more readable
As mentioned this becomes cumbesome to maintain with large numbers of arguments. Interpolation solves this problem, however, it introduces the requirements that all arguments are variables, unless you allow complex expressions between "{string.Join(',', list)}" which I do not suggest as you'd be writing C# while inside string quotes, thus making it difficult for the IDE to implement any kind of completion/intellisense while writing this expression inside the quotes. But getting to my intended point, if you want to eliminate the dependency on declaring variables then adopt a syntax that emulates named parameters. Imagine if you could do something like this with string.Format:
string.Format( "My name is {name} and I am this many {age}",
    name: person.FirstName + " " + person.LastName,
    age: person.YearsOld )
I"m not proposing this as the syntax, but just to exemplify the key features here I'd like to see in the syntax 1) the mapping of a {fieldname} to another syntax element fieldname: someExpression which seperates the expression from the string itself giving it only a name, and 2) also ensures no local variable is needed to bind to the fieldname.

This gives you the best of both worlds, with the exception of being a bit verbose. In this case, the verbosity is kept outside of the string itself, so you still get great readability and arguably better arguability than embedding expressions since all {variable} instances are kept to a single identifier.

If you don't allow an easy way to pull expression OUT of the string but also without forcing use of a local variable, then people will have a tendancy to put expressions inside the string, which will gradaully lead you back to the same issue with string concatenation where your string is constantly interupted by large expressions.

Of course some will argue that we can't force people to write readable code, but we can encourage a pit-of-quality and help lead them to it and certainly make it easier for those who want to do so.

Another reason to provide a way to pass expressions out of channel and NOT require local vars is for linq projections.
//convert all failed people found into messages
db.People.Where(p=> p.IsFailed)
.Select( p => new {
   Message = string.Interpolate( "{name}'s account is in a failed state due to error: {errorMessage}", name: p.First + " " p.Last, errorMessage: p.Error,
   ...
}).ToList();
Regarding the issue "it's hard to draw a clear compiler-enforced line between "things that are okay in holes" and "things that aren't"", if you only allow single identifiers like varaiables/param names, then in the above even something as simple as p.Error would not be possible as I'd have to stage p.Error into some variable to accomplish this.

I hope you can see the value of this proposed concept of allow expressions but forcing them to be seperated by some sort of named param (I say named param, but hopefully one reallizes I don't mean that in the strict sense of leveraging the existing C# named param feature). This mapping will allow both easily readable self documenting strings, reordering of params since they are named, and allow one to easily see where each param comes from without having to count the params as you would with {0}.

How nice it would be if instead of
string url = string.Format("{0}://{1}.{2}/{3}", someScheme, someSubdomain, Request.Current.Host, relativeUrl);
I could make the string itself more readable:
string url = string.Format("{scheme}://{sub}.{host}/{relUrl}", scheme: someScheme, sub:someSubdomain, host:Request.Current.Host, relUrl, relativeUrl);
Cheers
Jul 27, 2014 at 10:46 PM
Aaron,

Reading your post I began to thing about params dictionary. It would be pretty much like params array since 1.0 and params enumerable in 6.0.

Using the syntax you propose, the compiler would build an IDictionary<string, string>. For any method, not string.Format in particular.

One of the overloads of string.Fromat would take an IDictionary<string, string> and use it like you propose.
Jul 27, 2014 at 10:55 PM
PauloMorgado wrote:
Aaron,

Reading your post I began to thing about params dictionary. It would be pretty much like params array since 1.0 and params enumerable in 6.0.

Using the syntax you propose, the compiler would build an IDictionary<string, object>. For any method, not string.Format in particular.

One of the overloads of string.Fromat would take an IDictionary<string, object> and use it like you propose.
Jul 28, 2014 at 8:06 AM
PauloMorgado wrote:
Aaron,

Reading your post I began to thing about params dictionary. It would be pretty much like params array since 1.0 and params enumerable in 6.0.

Using the syntax you propose, the compiler would build an IDictionary<string, string>. For any method, not string.Format in particular.

One of the overloads of string.Fromat would take an IDictionary<string, string> and use it like you propose.
IMO that would be much more useful than params IEnumerable...
Jul 28, 2014 at 9:30 AM
tom103 wrote:
IMO that would be much more useful than params IEnumerable...
IEnumerable is very useful when you already have the IEnumerable, like from a LINQ query.
Jul 28, 2014 at 9:37 AM
PauloMorgado wrote:
IEnumerable is very useful when you already have the IEnumerable, like from a LINQ query.
It's nice, but it just saves you a call to ToArray... (OK, calling ToArray also causes immediate execution of the query, but I don't think many methods rely on the list of arguments being lazily evaluated).
Jul 28, 2014 at 3:19 PM
tom103 wrote:
PauloMorgado wrote:
IEnumerable is very useful when you already have the IEnumerable, like from a LINQ query.
It's nice, but it just saves you a call to ToArray... (OK, calling ToArray also causes immediate execution of the query, but I don't think many methods rely on the list of arguments being lazily evaluated).
There must have been enough requests for it. I'm sure it wasn't done just for the fun of it. Right, fellow developers?
Jul 30, 2014 at 8:40 AM
It seems like the real problem we're trying to solve isn't trying to completely replace Composite Format Strings -- developers can always write their own code to parse a string and replace variables if they want really fancy semantics in the holes.

The real problem is that you have to mentally jump back and forth between the holes in the format string and the position of the corresponding other String.Format arguments.

I don't think a new language feature to embed code inside literal strings is the way to go. Use of literal strings should be an edge case, and giving literal strings a strong advantage for "interpolation" would encourage "magic string" anti-patterns.

Here's the sort of sugary-sweet call I think would be useful, and would still be able to take a variable template:
String.Format(
     "Your name is {name} and your birthdate is {birthdate:yyyy-mm-dd}",
     name:      user.name,
     birthdate: user.birthdate
);
To make this happen, the C# language could be extended to support a more evolved version of the params keyword that allows the caller to pass a dynamic number of named parameters of a given type:
String.Format(string format, namedparams object[] args);
Inside the method, this would be exposed as, say, a List<KeyValuePair<string, object>>.

This could be used in situations other than string formatting. It would be a good way to evolve the (relatively recent) named argument capability in C#, and it would be relatively easy to extend to VB as well.

Now, if you want to have some really fancy custom logic in how the holes are interpreted, another overload of String.Format would be useful, and wouldn't require any change to the language:
public interface IFormatItemResolver {
    public object GetValue(string formatItemName);
}

String.Format(string format, IFormatItemResolver)
This would be analogous to the RegEx.Replace overloads that take a MatchEvaluator argument. I don't think this solves the simpler case that named parameter lists would, but it would give people more options for dynamic replacement without reinventing the wheels inside of String.Format.
Jul 30, 2014 at 9:58 AM
Edited Jul 30, 2014 at 9:58 AM
richardtallent wrote:
String.Format(string format, namedparams object[] args);
Inside the method, this would be exposed as, say, a List<KeyValuePair<string, object>>.
It would be very confusing to have a different type from the caller perspective and inside the method... And anyway I don't see how that would work at the CLR level. Why not just declare args as a dictionary?
public static string Format(string format, params IDictionary<string, object> args);
Jul 30, 2014 at 12:06 PM
public static string Format(string format, params IDictionary<string, object> args);
A beautiful syntax for an excellent idea. This can be very useful for many functions, way beyond string interpolation.
Jul 30, 2014 at 12:30 PM
You don't really need params IDictionary, you can use anonymous classes like here: http://blog.dotnetwiki.org/2009/01/16/NamedFormatsPexTestimonium.aspx

Anyway, one small problem with this approach is that Translation software will confuse the variable identifiers and translate them too:

Bing translator EN -> ES

Hi {0} -> Hola {0}
Hi {name} -> Hola {nombre} // bang!

So If this gets implemented somebody should tell, at least, Bing Translator team
Jul 30, 2014 at 3:09 PM
Edited Jul 30, 2014 at 3:09 PM
Olmo wrote:
You don't really need params IDictionary, you can use anonymous classes like here: http://blog.dotnetwiki.org/2009/01/16/NamedFormatsPexTestimonium.aspx
Or like here: https://github.com/thomaslevesque/NString#stringtemplate
But the trouble with this approach is that it uses reflection, so it's much slower... In my implementation, I maintain a cache of property getters to improve performance, but it's still about 5 times slower than String.Format. The dictionary approach wouldn't have this problem.
Anyway, one small problem with this approach is that Translation software will confuse the variable identifiers and translate them too:

Bing translator EN -> ES

Hi {0} -> Hola {0}
Hi {name} -> Hola {nombre} // bang!

So If this gets implemented somebody should tell, at least, Bing Translator team
I've been using the StringTemplate mentioned above in production apps for almost two years, and so far there hasn't been a single case where the translators tried to translate the identifiers. They seem to understand that the braces mean "do not touch" ;)
Jul 30, 2014 at 3:21 PM
tom103 wrote:
It would be very confusing to have a different type from the caller perspective and inside the method... And anyway I don't see how that would work at the CLR level. Why not just declare args as a dictionary?
I agree that we shouldn't imply an array (the "[]") and then expose a List -- the danger of typing this at 3:30am I'm afraid.

I like that you've overloaded params with a specific type rather than creating a new keyword. I also like that IDictionary<>, like my version, allows other uses beyond String.Format that may want a different parameter value type than object.

But I do have some concerns about using IDictionary<> this way, and think a new type is called for:
  1. Because of the nature of params, the key will always be a string, so we shouldn't require/allow that type to be specified in the method definition.
  2. String.Format performance is important, and the creation of a concrete dictionary and using it within String.Format may not be the best strategy. We should let String.Format (and other methods taking in named parameters) decide if it's worth the effort to deal with buckets, hash codes, etc. vs. a simpler approach.
  3. Because the compiler is generating a concrete object to pass as the "real" argument, we don't need to use an interface here, we can have a generic type that is fit for purpose.
  4. IDictionary<> is defined in System.Collections.Generic, I'm not so sure Microsoft would be interested in having such a low-level reference to it baked into the language. Same goes for List<> and KeyValuePair<>.
  5. While String.Format doesn't, some users of these parameters may expect them to be accessed in the same order as they were provided in the call. A Dictionary won't guarantee that.
The existing params functionality uses arrays, and that would be fine for this purpose as well. The called method can decide if and when to convert the array to another data structure, such as a dictionary, if it will likely improve performance.

But if we pass an array, we need a structure for our keys and values. Simple enough:
struct NamedParam<T> {
    public string Key;
    public T Value;
}
Now our method definition looks like this:
public static string Format(string format, params NamedParam<object>[] args);
The only down side here is that the compiler would need to resolve that NamedParam<T> is a special type and that the arguments will look different than normal params. A new keyword like namedparams might be needed to resolve this.

@eldritchconundrum, I agree that named variable parameters could be incredibly useful for other purposes. :)

@Olmo, translation is a good point. Anonymous types would work if you could pass them. But even if you could, you'd have to use reflection to get to the keys, which would be messy.
Jul 30, 2014 at 4:01 PM
richardtallent wrote:
  1. Because of the nature of params, the key will always be a string, so we shouldn't require/allow that type to be specified in the method definition.
Well, you can always declare an interface like that:
public interface INamedParameters : IReadOnlyDictionary<string, object> { }
(making it a read-only dictionary seems reasonable since it wouldn't make sense for the callee to modify the values)
  1. String.Format performance is important, and the creation of a concrete dictionary and using it within String.Format may not be the best strategy. We should let String.Format (and other methods taking in named parameters) decide if it's worth the effort to deal with buckets, hash codes, etc. vs. a simpler approach.
Hard to be sure without benchmarking, but I agree that, since there will typically be few arguments, the dictionary created by the compiler should probably be implemented as an array or a linked list, rather than a hashtable. Anyway, that's an implementation detail that will have to be worked out by the team.
  1. Because the compiler is generating a concrete object to pass as the "real" argument, we don't need to use an interface here, we can have a generic type that is fit for purpose.
I think it's cleaner to use an interface; I don't see any good reason to use a concrete type instead
  1. IDictionary<> is defined in System.Collections.Generic, I'm not so sure Microsoft would be interested in having such a low-level reference to it baked into the language. Same goes for List<> and KeyValuePair<>.
I don't think it matters; the planned features for C# 6 already include params IEnumerable<T>, and IEnumerable<T> is also defined in System.Collections.Generic.
  1. While String.Format doesn't, some users of these parameters may expect them to be accessed in the same order as they were provided in the call. A Dictionary won't guarantee that.
I don't understand what you mean; the order would matter if the parameters were evaluated lazily, but that's not the case: they will be evaluated before the method invocation anyway, so why would it matter that they're accessed in a different order? And anyway, Dictionary<TKey, TValue> doesn't maintain the order (because it's implemented as a hashtable), but other implementations could.
The existing params functionality uses arrays, and that would be fine for this purpose as well. The called method can decide if and when to convert the array to another data structure, such as a dictionary, if it will likely improve performance.

But if we pass an array, we need a structure for our keys and values. Simple enough:
struct NamedParam<T> {
    public string Key;
    public T Value;
}
Now our method definition looks like this:
public static string Format(string format, params NamedParam<object>[] args);
The only down side here is that the compiler would need to resolve that NamedParam<T> is a special type and that the arguments will look different than normal params. A new keyword like namedparams might be needed to resolve this.
An array doesn't let you easily fetch the value associated with a key; that would be quite inconvenient...
Anonymous types would work if you could pass them
But you can pass them; an instance of an anonymous type is just like any other object. The only issue is that you can't access the definition of the type, so you have to declare the parameter as object or make the method generic; any of these would work :
public static string Format(string format, object parameters);
public static string Format<T>(string format, T parameters);
Jul 30, 2014 at 4:19 PM
Edited Jul 30, 2014 at 4:25 PM
richardtallent wrote:
Here's the sort of sugary-sweet call I think would be useful, and would still be able to take a variable template:
String.Format(
     "Your name is {name} and your birthdate is {birthdate:yyyy-mm-dd}",
     name:      user.name,
     birthdate: user.birthdate
);
This is pretty much exactly what I suggested, I think? That example pretty much covers what I feel would be really great. The string is compact and readable, all {} holes are self documenting, any expressions are abstracted out to the named params to ensure they don't break up the readability of the string. It also forces you to give an expression some arbitrary name, which improves the self documenting aspect of the code since sometimes people in a hurry embed expressions in string concatenation which are not completely self explanatory.

I could actually implement this very easily using a Roslyn code transformation extension. You take calls like this:
String.Interpolate(
   "Your name is {name} and your birthdate is {birthdate:yyyy-mm-dd}",
   birthdate: user.birthdate
   name:      user.name,
);
Parse the holes in the string, assign each an integer. Using my list of name/int pairs, I replace each name with the int, and reorder the named params into a params array based on their assigned int index:
  String.Format( "Your name is {0} and your birthdate is {1:yyyy-mm-dd}", 
     new object[] { user.name, user.birthdate };
So it is purely syntactic sugar. No CLR change needed at all!

Note I am not necessarily proposing String.Interpolate( as the syntax for this feature. It would certainly be weird for the compiler to do special handling for a specific method call of a certain Name. Just presented it that way because it's easiest to show as a proof-of-concept that it could be implemented without a CLR change.
Jul 30, 2014 at 5:34 PM
tom103 wrote:
But if we pass an array, we need a structure for our keys and values. Simple enough:
Having the structure include Type as well could be useful for some situations, such as in logging utilities (where it may be useful to distinguish between e.g. a null string, a null reference to a control, and an int? whose HasValue is false. My preference would probably be to say that a parenthesized expression that may contain commas will match an overload parameter ParamInfo params params[] [moving params in the middle would make clear that even if the argument was of type ParamInfo[] it should still be wrapped in another ParamInfo[] array]. The name associated with each item would be the expression used to produce it unless the item started with a name and a colon, in which case the text before the colon would be the name.

Although retrieving items from arrays is generally less efficient than retrieving items from a Dictionary, for typical numbers of arguments it would likely be cheaper to use an array and a linear search than to construct a hash table which would be used once and discarded. Especially given that items would often be used in order, a linear search which always starts immediately following the previous item found and is willing to wrap around once would almost certainly be faster in 99% of applications than would a Dictionary.
Jul 30, 2014 at 5:52 PM
Edited Jul 30, 2014 at 8:29 PM
supercat wrote:
for typical numbers of arguments it would likely be cheaper [...] than to construct a hash table which would be used once and discarded.
Agreed, for very low number of arguments linear search would probably be faster(benchmarking of course to confirm). This is often true for any algorithm designed to scale well. There are some O(n) algorithms that perform better than O(1) algorithms for a small enough list. This is why many implementations for quick sort will use a different algorithm for handling smaller lists than for larger lists.

Given the use cases for this feature, params count should be low. If someone is building a really huge string with a very larger number of holes, they 1) Probably shouldn't be inlining that string as a constant 2) use a lightweight templating framework like dotliquid.

Given that, the performance implications of argument counts should be considered negligible IMHO. So the design should consider other factors for performance/optimization as well as prioritizing ease-of-use. I.e. if the consensus is one design has better ease-of-use, I'd be in favor of that design over one that is simply designed to be more performant for a very large number of params.
Jul 30, 2014 at 9:00 PM
AaronLS wrote:
Parse the holes in the string, assign each an integer. Using my list of name/int pairs, I replace each name with the int, and reorder the named params into a params array based on their assigned int index:
Good idea for a proof of concept, but unless I'm mistaken, wouldn't that only work for literal strings, since it's happening at compile time?
Jul 30, 2014 at 9:41 PM
supercat wrote:
But if we pass an array, we need a structure for our keys and values. Simple enough:
Having the structure include Type as well could be useful for some situations
I'm not following. My example NamedParam<T> is generic for the value type, so String.Format can use object (as it is wont to do) but other uses of the variable named parameters could be of another type.
Although retrieving items from arrays is generally less efficient than retrieving items from a Dictionary, for typical numbers of arguments it would likely be cheaper to use an array and a linear search
Yup, I came to the same conclusion.

A naive O(n) search could be added to the array via an extension method to provide Dictionary-like access:
    public static bool TryGetValue<T>(this NamedParam<T>[] array, string key, out T result) {
        if(key!=null && array!=null) {
            foreach(var kv in array) {
                if(kv.Key==key) {
                    result = kv.Value;
                    return true;
                }
            }
        }
        result = default(T);
        return false;
    }
A more robust approach would be to, say, have an extension method that returns a new "searcher" object that can have its own state so it can implement searches using a hashtable, look-after-last-found, or other algorithm.
Jul 30, 2014 at 9:47 PM
Side note -- should we move this to another thread to talk more generally about the idea of variable named parameters?
Jul 31, 2014 at 3:45 AM
First, can I get some concensus from others on one issue. It seems most of us currently active in the discussion want some variation of named params to support this. Primarily, I don't think any of us want to be forced to declare variables for every single hole, and we also recognize the risks and complexity of allowing expressions inside the string so we are in favor of the {} holes being simple identifiers. This led us to considering some form of named params to support these desires.

None of us want it to be like this, correct? In terms of being forced to declare variables:
string name = user.Name;
string birthdate = user.Birthdate;
string interpolated = $"Your name is {name} and born {birthdate}";
My fear is this is how they are planning to implement it, based on the OPs post. I see mention of the design having been "nailed down" but I don't see where they publicized their ultimate decision. I am a bit fearful. Once they implement this and release it, I fear what we are proposing is too much of a feature overlap and yet not an interative tweak/improvement of their syntax, such that our approach would never be considered.

richardtallent wrote:
AaronLS wrote:
Parse the holes in the string, assign each an integer. Using my list of name/int pairs, I replace each name with the int, and reorder the named params into a params array based on their assigned int index:
Good idea for a proof of concept, but unless I'm mistaken, wouldn't that only work for literal strings, since it's happening at compile time?
Yes that is a limitation, but also means you get some compile time verification that the names in the literal string match up with the named params. All the strings where I've ever used string.Format with "{0}" holes were literals. The parts that were dynamic, were the parts that went into the holes. As OP points out though, there will be some using resources(in 10 years and 4 places, I have yet to work somewhere that was building an internationalized app and so resources were never used).

If you wanted a feature like this which is evaluated at runtime, you can accomplish that today without a great deal of difficulty(there are already templating libraries that do almost exactly this):
string templateMaybeFromResource = "Your name is {name} and your birthdate is {birthdate:yyyy-mm-dd}";
string rendered = Library.Interpolate("Your name is {name} and your birthdate is {birthdate:yyyy-mm-dd}",
    new Dictionary<string, string>{
        { "name", user.Name },
        { "birthdate", user.Birthdate },
    });
or dotliquid style with anonymous type(this requires reflection to work however:
string rendered = Library.Interpolate("Your name is {{name} and your birthdate is {birthdate:yyyy-mm-dd}",
    new {
        name, user.Name ,
        birthdate, user.Birthdate
    });
The only substantial thing extra is the new Dictionary<string, string>{. If we had list initializers for generic types which inferred the type , then that'd cut it down to new Dictionary{.

It seems most of us currently active here are in favor of having very simple named {} holes, with some sort of named list/params of expressions outside of the string. As opposed to embedding expressions into the string, and also opposed to being required to have variables/params declared that match the named holes(which is what seems to be implied in the OP's post).

We differ on how we propose we get there, mainly into two big groups:
Camp A: Make passing this variable list of named parameters easier. This would be syntactic sugar to lighten the syntax for the list initializer. Within this camp are differing opinions on what the syntax for this should be, and what list/array type should actually be used.
-Pro: Applicable to a wider variety of scenarios beyond string interpolation.
-Con: No guarantee the holes in the string are valid and match the given names at compile time.

Camp B: (not sure anyone else is in this camp besides me) A syntax specific and applicable only to string interpolation, with the string literal evaluated at runtime.
-Pro: Compile time safety against the string literal.
-Pro: Potential for optimizations, inlining references to constants, etc.
-Pro: Could invent a very terse syntax that doesn't even involve a function call "Your name is {name} and born {birthdate}"~> new { name: user.Name, birthdate = user.Birthdate }
-Con: Works only with string literals.

They are not necessarily mutually exclusive, you could implement A and also implement compile time verification of the string and potential optimizations.
1) Both A and B provide the same ease of use for string interpolation
2) A is applicable/useful to a much wider set of scenarios
3) B is limited to compile time interpolation

I am honestly on the fence about it now. I'd be happy to see either, so long as I don't have to declare local variables, as the OP seems to imply. Both A and B are probably negligible difference in terms of effort to support. I could do the same proof-of-concept code transformation to replace named params with a list initializer, mainly because named params are already a syntax roslyn supports.

A seems a winner because it is applicable to a much wider scenario. However, it doesn't accomplish a great deal more than simply making list initializers for named pairs a little more terse syntax wise IMO.

My only love for B is my love of the compiler doing anything and everything it can to catch things upfront.
Jul 31, 2014 at 4:38 AM
I got to thinking about this tonight again, and I wonder -- would the C# team really be interested in adding functionality to params, or is it just there for feature parity with C++'s va_arg?

If there is concern adding this feature, an excellent compromise (that would be incredibly useful in other situations, even more than variable named parameters) would be implied collection initialization. This would just be syntactic sugar for collection initialization where the result is a known type.

Here would be the new overload in String:
public static string Format(string format, Dictionary<string,object> args);
And here's how we would call it:
var result = String.Format(
     "Your name is {name} and your birthdate is {birthdate:yyyy-mm-dd}", {
        { "name":       user.name },
        { "birthdate": user.birthdate }
});
All we're getting rid of is the extra new Dictionary<string,object>. I realize this adds complexity to choosing the right overload, but this decision would be made by the compiler, not the runtime.

Going further, it would be even more useful for the compiler to be able to convert an anonymous type to a collection initializer on compilation, basically constructing the above (complete with the new Dictionary<string,object>) from a call like this:
var result = String.Format(
     "Your name is {name} and your birthdate is {birthdate:yyyy-mm-dd}", {
     name:       user.name,
     birthdate: user.birthdate
});
Boom. No more braces around the properties or delimiters around the names, and since the compiler is just rewriting the anonymous type as a initialized Dictionary<string,object> instance, there are no changes to the runtime and no new keywords needed. I can think of a thousand uses for this, it would essentially make creating Dictionary<string,T> instances nearly as simple as JavaScript associative arrays.

And really, there's no reason why this should be tied specifically to System.Collections.Generic.Dictionary. The compiler needs to know which specific class to instantiate and initialize (not just the interface), but the method could specify any class that implements IDictionary with a string key and value type that is compatible with the anonymous type's values.
public class MyDictionary<T> : IDictionary<string, T> {}

MyDictionary<string> colors = { 
    red: "#FF0000", 
    blue: "#0000FF", 
    green: "#00FF00"
}; 
This puts the power in the hands of the String.Format implementers to decide what the default passed collection type should be -- they may choose Dictionary, or they may choose something that implements IDictionary<string,object> without the hashtable overhead. (They could also provide an IDictionary<string,object> overload for developers who want to pass in some other means of doing the named lookup).

Despite the necessary extra brace pair, I think I would love having this even more than variable named parameters, and for String.Format, it would be a great way to solve issue at hand.
Jul 31, 2014 at 4:46 AM
Camp A: this is what I had in mind when I wrote the OP and described the "Python" approach. Several people have written in this thread to say they already use this in their production code.

It would really take off with a convenient syntax for writing dictionary literals. For instance, imagine if {name:"Jones", age:15} was a shorthand notation for constructing a Dictionary<string,object>. Then you could write an extension method that makes this work:
"Hello {name} you are {age} years old".Format( {name:customer.Name, age:customer.Age} );
Compile-time checking would still be fine. With Roslyn "analyzers", it's easy to write code analyzers, and we're encouraging library-authors to also implement library-specific diagnostics. In this case the library that contains the extension method "String.Format" would also contain a diagnostic that parses the string (if it's a constant string literal) and would provide warnings if the dictionary keys didn't match (if it was a dictionary literal).



Many of the C# language design team folks are out on summer holidays at the moment. The language design meeting won't re-convene for another 3-4 weeks.


However, I disagree with the premise this branch of the discussion has taken. The code above just isn't as convenient as string interpolation. The following reads MUCH more easily.
$"Hello {customer.Name} you are {customer.Age} years old"
I think it'd be ideal to add both string interpolation and dictionary literals.
Jul 31, 2014 at 4:56 AM
richardtallent wrote:
And really, there's no reason why this should be tied specifically to System.Collections.Generic.Dictionary.
You'd certainly want the dictionary literal to a type (presumably either System.Collections.Generic.Dictionary or ImmutableDictionary) if none is provided by the context:

var x = { red:"#FF0000", green:"#00FF00"};


Beyond that, you'd certainly want the dictionary literal to work with both Dictionary and ImmutableDictionary at a bare minimum. So yes, as you described, if the context provides a type that's compatible, I agree it should pick that up.
Jul 31, 2014 at 5:01 AM
Edited Jul 31, 2014 at 5:20 AM
AaronLS wrote:
First, can I get some concensus from others on one issue. It seems most of us currently active in the discussion want some variation of named params to support this.
Yup.

It appears we were both writing our responses at the same time, since I didn't see yours until I posted.

I understand the potential up-side in terms of compiler validation when using string literals, but that would also mean that the compiler would have to be hard-wired to look for the holes and corresponding arguments passed to all of the BCL methods that use the .NET Composite String Format syntax (it's not just String.Format, there are a number of others -- see here for more details).

That's a ton of compiler work to support only a few methods, and severely limits the usefulness for other places where one might want to use a sweeter way of passing names and values (or, as my last message describes, initializing string-keyed dictionaries, whether for a method call or not).

I've seen a recent demo for adding a VS plugin to detect and correct various code smells. If this were implemented in a way that follows the "A" camp (generally applicable and can be used on non-literal strings), it may be still possible to add IDE / compiler warnings when you are formatting a string literal and the key names don't match the string "holes."
I could do the same proof-of-concept code transformation to replace named params with a list initializer, mainly because named params are already a syntax roslyn supports.
I looked at some sample Roslyn transformation code and ran screaming from the room. Does the method I'm suggesting above (transforming from an anonymous type to an appropriate IDictionary implementation) seem reasonably possible?
Jul 31, 2014 at 5:18 AM
Edited Jul 31, 2014 at 5:21 AM
ljw1004 wrote:
You'd certainly want the dictionary literal to a type (presumably either System.Collections.Generic.Dictionary or ImmutableDictionary) if none is provided by the context:

var x = { red:"#FF0000", green:"#00FF00"};

Beyond that, you'd certainly want the dictionary literal to work with both Dictionary and ImmutableDictionary at a bare minimum. So yes, as you described, if the context provides a type that's compatible, I agree it should pick that up.
For var assignments, I think an anonymous type is still the correct result, even without the "new" keyword.

What I'm suggesting is just that we "borrow" the syntax of anonymous types to instantiate and initialize specific types that implement IDictionary<string,T> when that type is implied by the context (and the values of the anonymous type's properties can be assigned to T of course).

Now if we said IDictionary<string,string> x = { red:"#FF0000", green:"#00FF00"};, a Dictionary<string,string> does seem like a reasonable default. Same goes if we're trying to pass it to a method and the "best" overload (i.e., not object) is IDictionary<string,string>.
Jul 31, 2014 at 5:30 AM
The first example is definitely nice.

$"Hello {customer.Name} you are {customer.Age} years old"

I don't find this terrible, but my reasons for not preferring it:
1) I find my parameters to string.Format often involve expressions, not just identifiers. (and expressions shouldn't be allowed in the string)
2) Anything beyond a single identifier could be an invitation for trouble. Say you save some user's message to a database but using interpolation on it first. Perhaps the literal is "User left note {Message.Note}". That is safe, you decided what the string literal was for interpolation, so you know the only information exposed is Message.Note. BUT later on you retrieve this from DB, and use string interpolation on it again to embed that string into another string. Anytime you have a scenario where something could be interpolated twice(perhaps non obviously), the second interpolation is dangerous. This is because the user could have entered "{Request.Context.SomeSensitiveServerConfig}" which would do nothing on the first interpolation, but the second would then expose the value of that property. (I know some people cache information/configuration in properties for efficiency.)
3) Even with a single identifier, double interpolation might expose a local variable or field int he context of the current class.

The named params examples make it explicit what you expose to the interpolation. Safe templating engines always require you to opt-in to exposing data to the template. The named params is this opt-in facility.
Jul 31, 2014 at 5:37 AM
AaronLS wrote:
DOUBLE INTERPOLATION
2) Anything beyond a single identifier could be an invitation for trouble... Anytime you have a scenario where something could be interpolated twice(perhaps non obviously), the second interpolation is dangerous. This is because the user could have entered "{Request.Context.SomeSensitiveServerConfig}" which would do nothing on the first interpolation, but the second would then expose the value of that property.
That's not at all what's being proposed. String interpolation, as proposed, is a compile-time transformation of the $"xyz" string literal that was written in the source code. It only ever looks at the {...} holes in the literal. Its behavior can never be influenced by the runtime values of any strings. The "double-interpolation" scenario you describe is impossible.

Please try writing out your example step by step, showing every statement that gets executed, and we can pinpoint precisely where.


I still disagree with your point 1. Consider:
var msg = "There are { reds.count + blues.count } balls in the jar";
var msg = "There are {count} balls in the jar".Format( {count : reds.count + blues.count} );
The first one reads more fluently. They're both doing exactly the same thing, but the second one introduces an extra layer of indirection that stops you reading it as easily, and forces you to invent a name "count" for something that you normally don't care to name.
Jul 31, 2014 at 6:04 AM
Edited Jul 31, 2014 at 6:05 AM
ljw1004 wrote:
var msg = "There are { reds.count + blues.count } balls in the jar";
I agree that it's fluent, but here are my big concerns:
  • We're getting dangerously close here to re-implementing ASP.NET's <%=%> syntax, within a string instead of a page.
  • This is "inception code" -- code within a string within code. This means IDE support for things like refactoring, autocompletion, etc. become a lot harder.
  • What types of expressions are supported? Can someone, say, put a LINQ statement within the braces? What if someone wants to use a switch{} statement or something else that is a valid C# expression but requires the use of its own braces? What if someone tries to embed another string literal inside the expression? What if that string literal contains its own "holes"?
  • How are cultural settings applied to the implied ToString() calls that the holes get translated into?
  • Having such a deep language feature that only work with literal strings not only blocks out a large number of potential uses, it actively encourages coding using string literals for all sorts of situations where the string template really shouldn't be embedded in the codebase. For example, I can definitely see novice (or lazy) developers trying to use this to create dynamic SQL, which could be very, very bad.
  • This basically creates an alternative to String.Format and other CFS-based methods rather than just fixing them.
Jul 31, 2014 at 7:02 AM
richardtallent wrote:
  • We're getting dangerously close here to re-implementing ASP.NET's <%=%> syntax, within a string instead of a page.
Arguably a good thing! Also re-implementing VB's <%= %> syntax as well.
  • This is "inception code" -- code within a string within code. This means IDE support for things like refactoring, autocompletion, etc. become a lot harder.
Try using VB's <%= %>. The intellisense is fine!
  • What types of expressions are supported?
Proposal for C# is to only allow dot-separated identifiers and nothing else, at least as a first step, since this is easy to implement.

Swift allows any expression so long as it doesn't contain a quote character or a newline.

VB's <%= %> allows arbitrary expressions.

I think VB has the right idea. It's up to you the developer not to shoot yourself in the foot. Developers can go crazy in a whole bunch of places (e.g. you can pass lambdas inside lambdas inside lambdas as an argument to a method??! you can put await in arbitrary contexts e.g. "f(ref arr[await t])" ??!). I think it always makes for a cleaner language when you make it fully compositional, so you can put anything in, and leave it to developers to keep it sensible. They generally will, as we've seen.
  • How are cultural settings applied to the implied ToString() calls that the holes get translated into?
Proposal is to use the current culture. That's what .ToString() does. That's what String.Format() does if you don't pass extra arguments.
  • I can definitely see novice (or lazy) developers trying to use this to create dynamic SQL, which could be very, very bad.
Proposal is to allow e.g. Foo$"from {x} in {y} select {z}" which gets translated into Foo("from {0} in {1} select {2}", x, y, z). This is a direct copy of what Javascript will do to, for precisely the reason you mentioned. The "Foo" can be any method or delegate. It will be able to escape everything as needed. This mechanism can also be used to pick something other than the current culture.

With this kind of guard, I think it's no longer "very very bad". On the contrary it becomes a clean readable syntax that's also safe.
  • This basically creates an alternative to String.Format and other CFS-based methods rather than just fixing them.
Rather, proposal is to be syntactic sugar for String.Format; not an alternative.
Jul 31, 2014 at 11:51 AM
ljw1004 wrote:
richardtallent wrote:
  • What types of expressions are supported?
Proposal for C# is to only allow dot-separated identifiers and nothing else, at least as a first step, since this is easy to implement.

Swift allows any expression so long as it doesn't contain a quote character or a newline.
The complicating matter here it seems are the formatting expressions used by composite formatting which can produce potential ambiguities, no?. I wonder, however, how many of those ambiguities could be eliminated by requiring that any expression more complicated than a simple member access be wrapped in parenthesis as well as braces.

var s = $"x is less than y: {(x < y ? "true" : "false")}"
  • How are cultural settings applied to the implied ToString() calls that the holes get translated into?
Proposal is to use the current culture. That's what .ToString() does. That's what String.Format() does if you don't pass extra arguments.
Which also seems to be what every other language which incorporates this feature does. Right or wrong, it's consistent with both .NET and the rest of the ecosystem.
Jul 31, 2014 at 4:27 PM
ljw1004 wrote:
That's not at all what's being proposed. String interpolation, as proposed, is a compile-time transformation of the $"xyz" string literal that was written in the source code. It only ever looks at the {...} holes in the literal. Its behavior can never be influenced by the runtime values of any strings. The "double-interpolation" scenario you describe is impossible.
What would you think of having the a syntax that would expand a specially marked string, passed as a parameter, into a sequence of parameters, starting with a string, such that saying:
st = String.Format(<$"There are "{count}" balls in the {jar}, at a price of "{pennies/100m,"0.00"}"."$>)
would translate as:
st = String.Format("There are {0} balls in the {{}jar}, at a price of {1:0.00}.", count, pennies/100m);

Basically, concatenating a brace-enclosed expression with the string would bump a counter n and insert "{n}". If the braces contained a comma and a string [literal or otherwise] the compiler would add a colon and the enclosed string after the number. Enclosing the main string in <$ $> would also cause brace characters within it to be escaped.
Coordinator
Jul 31, 2014 at 4:59 PM
supercat wrote:
What would you think of having the a syntax that would expand a specially marked string, passed as a parameter, into a sequence of parameters
Look in the OP, a few paragraphs down under "Javascript alternative". We discussed exactly this feature. We didn't like it because expanding it into a comma-separated list would not work for the simple case:
   var s = <$"There are {count} balls in the jar"$>;
Then it boils down to: how about having both string interpolation which implicitly expands into a call to String.Format, and a separate syntax (you suggest <$"..."$>, and we imagined $$"...") which expands into a comma-separated list and can only ever be used in function-call argument-list positions.

It doesn't really seem worth the complexity. It seemed like few people would use $$"..." if they already had $"...". And having two similar features in the language would be a bit ugly.
Jul 31, 2014 at 5:03 PM
Edited Jul 31, 2014 at 5:09 PM
ljw1004 wrote:
That's not at all what's being proposed. String interpolation, as proposed, is a compile-time transformation of the $"xyz" string literal that was written in the source code.
Yes you are correct, but if you follow the discussion you will see I'm very aware of this, yet there is a great deal of discussion here about providing named holes to something that evaluates at runtime similar to string.Format. I won't go into detail because there are numerous examples already in the thread of how this would be implemented and the feature parity it provides to the other features, as well as recognizing the differences. Just a couple posts before I distinguish the two approaches as Camp A and Camp B.
It only ever looks at the {...} holes in the literal. Its behavior can never be influenced by the runtime values of any strings. The "double-interpolation" scenario you describe is impossible.
I stated "you retrieve this from DB, and use string interpolation on it again". Clearly that's not a string literal, so clearly I'm referring to runtime interpolation.
I still disagree with your point 1. Consider:
var msg = "There are { reds.count + blues.count } balls in the jar";
var msg = "There are {count} balls in the jar".Format( {count : reds.count + blues.count} );
The first one reads more fluently. They're both doing exactly the same thing, but the second one introduces an extra layer of indirection that stops you reading it as easily, and forces you to invent a name "count" for something that you normally don't care to name.
I will acknowledge you're points are in the context of compile time literals. So I won't argue the points related to runtime evaluation.

Please read the points under the MS Developer's post(it's the second post) header "Q. Expressions in holes?" that gives some very good points of why this should not be allowed. One of the biggest ones being trying to parse C# from inside the string or provide intellisense will be very difficult.

The first example reads "There are the 5 + 6 balls in the jar." The goal of the string is to express to the total number of balls in the jar. How you got to that total is not communicated in the final string, so including that detracts from the readability. If you include expressions in the string, what are you buying yourself beyond what we already do with string concatenation:
var msg = "There are { reds.count + blues.count } balls in the jar";
var msg = "There are " + reds.count + blues.count + " balls in the jar";
You didn't get yourself very much with this feature this way. You still have a string interrupted continuously with expressions. What if the expression were:
"There are { balls.Count(b=>b.Color == Color.Red || b.Color == Color.Blue) } balls in the jar."
//versus
"There are {total} balls in the jar.".Interpolate( 
    {total : balls.Count(b=>b.Color == Color.Red || b.Color == Color.Blue) }
The second string is much more readable. That level of indirection you pointed out is a good thing. It's a separation of concern. The string is concerned with presentation, the named params concerned with calculation.
Jul 31, 2014 at 5:33 PM
What it buys you is simplicity and compile-time checking. There isn't supposed to be a separation of those concerns, at least not in this case. If you wanted to make it easier to embed the results of complicated expressions that's when it would be appropriate to define a temporary variable to house said value. Even if the holes did permit arbitrary expressions of any degree of complexity that should still be considered the best practice for exactly the reason you specified, reducing clutter.
var total = balls.Count(b=>b.Color == Color.Red || b.Color == Color.Blue)
var s = $"There are {total} balls in the jar.";
I do feel that named params is a neat feature that probably has a variety of uses. I also think that string.Format and other methods of composite formatting could use some forms of love in the framework. However those would be separate concerns from string interpolation which serves to fill the simple case.
Jul 31, 2014 at 5:42 PM
Edited Jul 31, 2014 at 5:46 PM
What if the type is dynamic?
dynamic obj = ...
string x = "What about { obj.method }?"
Alternative to named params would be array (or collection) or Key-Value pairs.
var dict = { (key0 : value) , (key1 : value) , (key2 : value) } /* Key-Value pairs */
Also String.Format Diagnostic can / could be extended to include named holes.
This will be provide the compile-time warnings.
Jul 31, 2014 at 6:39 PM
Edited Jul 31, 2014 at 6:40 PM
Halo_Four wrote:
What it buys you is simplicity and compile-time checking.
var msg = "There are { reds.count + blues.count } balls in the jar";
var msg = "There are " + reds.count + blues.count + " balls in the jar";
The first is no simpler than the second. That was the example I responded to, and it is no simpler. You've just traded +" for a {/}
Jul 31, 2014 at 6:40 PM
Edited Jul 31, 2014 at 6:46 PM
Making this blank and trying again to work around Codeplex bug
Jul 31, 2014 at 6:46 PM
If we're only talking about improving string literals (and I hope we're not but I'll bite), the real "clutter" we're trying to avoid is the character overhead of " + and + " when context-switching during string concatenation:
var msg = "There are " + (reds.count + blues.count) + " balls in the jar";
So let's forget about interpolation and String.Format and get back to the core problem:

How can we context-switch more fluidly between string literals and expressions when performing string concatenation?

One solution might be for C# to generally support postfix (RPN) notation of binary operators after a series of terms (used here for concatenation operators and strings):
var msg = "There are "reds.count" red balls in the jar"+;
Advantages of this approach:
  • Whitespace around expressions like red.count is optional, so we can have them tight with the surrounding string literals, as above, or broken down by lines, indented, etc. We can also use parenthesis around terms if it makes things clearer in a particular situation.
  • Full IDE support should be easy since the expressions are already outside of the string literals.
  • This should be easy to implement as a compiler transformation to convert to infix.
  • No overloading of new punctuation symbols, no new keywords.
  • Postfix notation of binary operator could be useful in other situations (math, etc.).
  • This is all inline concatenation, no calls to String.Format or similar methods.
  • The compiler could even be smart and decide to use StringBuilder depending on the number of terms.
  • It will make Forth developers and HP calculator users smile.
  • I can't think of any ambiguity this would create with currently-valid C# language constructs.
The formatting would be up to the developer. All of the below would be equivalent to the example above:
var msg = "There are " reds.count " red balls in the jar" +;
var msg = "There are "(reds.count)" red balls in the jar"+;
var msg = "There are "
    reds.count
    "red balls in the jar"+;   // line breaks
Jul 31, 2014 at 7:03 PM
Edited Jul 31, 2014 at 7:06 PM
How do we distinguish between the string
"There are { reds.count + blues.count } balls in the jar" /* You actually want this string */
and the interpolated string
"There are { reds.count + blues.count } balls in the jar" /* you want the interpolation of this string */
Currently with String.Format you have the context of the method to fall back on to determine "the meaning" of the string literal.

Side Note

Enable diagnostics to change the styling of the text, so you could have it say colourise the holes.
Jul 31, 2014 at 8:21 PM
lwischik wrote:
supercat wrote:
What would you think of having the a syntax that would expand a specially marked string, passed as a parameter, into a sequence of parameters
Look in the OP, a few paragraphs down under "Javascript alternative". We discussed exactly this feature. We didn't like it because expanding it into a comma-separated list would not work for the simple case:

```cs
var s = <$"There are {count} balls in the jar"$>;
If a programmer doesn't like typing String.Format add a mechanism to allow aliasing of free functions (something which IMHO should exist anyway: having to stick Math. in front of all the math functions in a block of code does nothing to improve readability).

What would you suggest would be the equivalent if code wanted to call someStringBuilder.AppendFormat(CultureInfo.InvariantCulture, ...) with an interpolated set of parameters, or otherwise needed to do something other than default formatting? Code which is constructing HTML strings could benefit from interpolated string syntax, but only if it can use "." as a decimal separator regardless of culture settings.
Aug 11, 2014 at 10:39 PM
I think you should just do $"..." for both languges. I would rather keep the contents of the strings as clean as possible so we don't have to litter it with \'s everywhere to use interpolation.
var x = $"{foo} {bar} {baz}"; // better
vs.
var x = "\{foo} \{bar} \{baz}";
For one thing it's less characters and typing, but second it seems less cluttered visually. Also backslash already has a meaning in strings as an escape character, now it will have a meaning as both an escape character and an interpolation character? I don't like that. Also with the \ you'd have the ability to combine it with @ which changes the escape semantics already, using $ would let you reduce the complexity there I would think. Just say it's always @ mode plus interpolation?
Aug 12, 2014 at 1:41 PM
With regards to localization I wanted to add an idea.
var x = ProductName$"Roslyn {version}";
Which would both generate a string into the invariant string resource table with "Roslyn {0}" as well as generate the interpolation code like so:
var x = String.Format(Resources.ProductName, version);
I don't know what the ideal syntax would be exactly but the general idea is that you could specify the resource name as well as the default string all inline.
Coordinator
Aug 12, 2014 at 3:46 PM
justinc wrote:
var x = ProductName$"Roslyn {version}";
Which would both generate a string into the invariant string resource table with "Roslyn {0}" as well as generate the interpolation code like so:
var x = String.Format(Resources.ProductName, version);
I don't know what the ideal syntax would be exactly but the general idea is that you could specify the resource name as well as the default string all inline.
That could be scary. Imagine
var x = ProductName$"Roslyn v{major}.{minor}";
   ...   "Roslyn v{0}.{1}"
   ...   "String.Format(Resources.ProductName, major, minor)"
Next I go ahead and localize my app, providing translations into other languages. Next I decide to revisit the English:
var x = ProductName$"Roslyn minor={minor}, major={major}";
At this point the positional orders will flip and all other languages will be wrong and I likely won't notice. It's scary for (1) the authoritative English text to be embedded in the source code while all other languages are stored in .xlf files, (2) for the positional placeholders to be critically important despite a C# syntax which makes it look as though positional placeholders are unimportant.
Aug 12, 2014 at 4:55 PM
I don't think the language should directly address the localization. Rather use the aforementioned Foo$"Roslyn {version}" which calls Foo with parameters for it. Then a library could handle something like this (although as mentioned localization shouldn't embed the english in the code).
Aug 12, 2014 at 7:57 PM

That was sort of the direction the discussions regarding named parameter lists were going. Instead of interpolation being a compile time language feature, it would be a runtime library feature(leveraging a new named param list language feature)

Aug 13, 2014 at 4:36 PM
AaronLS wrote:
That was sort of the direction the discussions regarding named parameter lists were going. Instead of interpolation being a compile time language feature, it would be a runtime library feature(leveraging a new named param list language feature)
I would like to see a named-parameter feature, perhaps using a special type
struct NamedParameter { public String expressionText; public Type expressionType; public Object value; }
and have a tag which would indicate that parameters should be placed into a NamedParameter[]. Unlike the parameter types Object and Object[], both of which can be satisfied by an array, NamedParameter[] could only be satisfied by an array of that exact type, and NamedParameter only by a structure.
Aug 13, 2014 at 6:26 PM
Edited Aug 13, 2014 at 6:26 PM
Surely the obvious design for such a struct would be
struct NamedParameter<T> {
    public string Name { get; private set; }
    public T Value { get; private set; }
}
which already exists (it’s called KeyValuePair<string, T>), but if you’re going to have that, you might as well make it an IDictionary<string, T>. Anything else would be stupid.
Aug 14, 2014 at 3:27 PM
Timwi wrote:
which already exists (it’s called KeyValuePair<string, T>), but if you’re going to have that, you might as well make it an IDictionary<string, T>. Anything else would be stupid.
The KeyValuePair<string,T> would be a fine choice if there were only one parameter, or if all parameters were of the same type, but neither condition will apply in most cases. IDictionary<string,T> would have at least three deficiencies:
  1. It doesn't provide any information about the sequence in which keys are specified, nor allow for the possibility that a parameter may be specified more than once.
  2. It doesn't provide any information about the type of a passed reference.
  3. Hash tables perform better than arrays if they contain many items, and are queried many times--a situation which simply wouldn't apply here.
BTW, with regard to point 2, there could actually be three types associated with a parameter. If a method which expects an Animal[] critters is given a Cat[], and critters[0] identifies a SiameseCat, then the compile-time type of storage location critters[0] would be Animal, the actual type of storage location critters[0] would be Cat, and the type of the object identified by that storage location would be SiameseCat. In some cases, it could be helpful for the called method to know when the compile-time type and actual type of the storage location don't match, but providing that information to the method would require using Reflection at run-time and would probably not be worth the effort.
Aug 19, 2014 at 5:03 AM
HI all, I think
  1. the default culture for this feature should be InviarantCulture because it is a compile-time feature, which means you cannot use it for resource localization. The major usage may be for URI combination and internal diagnotics strings only. under such circumstance, using InviarantCulture culcure is more suitable. Actually I think there will be few chance to use string interplization combined with CurrentCulture because the format string is not localizable any all.
  2. Is there any equivalent design for duplicated placeholders like string.Format("{0} is {0}", x)? The runtime value of the parameter will be computed only once in this mode, but if you using $"{x} is {x}" I believe it will be computed twice.
Nevertheless, if the feature is targeted for internal usage only (as I saied in the first point), this problem may be ignored because for most cases you will never display a value twice in URI or diagnotics string?

Best Regards,
Aug 21, 2014 at 1:40 AM
sgjsakura wrote:
  1. Is there any equivalent design for duplicated placeholders like string.Format("{0} is {0}", x)? The runtime value of the parameter will be computed only once in this mode, but if you using $"{x} is {x}" I believe it will be computed twice.
What makes you think that?
Aug 21, 2014 at 3:59 AM
PauloMorgado wrote:
What makes you think that?
Having such expressions always be computed twice would be less apt to cause "surprises" than having them be computed once if both occurrences were sufficiently similar, and twice if they differed sufficiently. If one sets down explicit rules as to when an expression will be computed once, and made clear that when those rules do not allow for common subexpression elimination the code must behave as though the expression is evaluated twice, the result might not be too bad, but such rules would add complexity for not much real gain. If one wants to allow expressions to be used more than once in a format string, it would be better to add a "tag" feature, and have what would be repeated invocations of the expression instead be references to the value of the first invocation.
Aug 21, 2014 at 8:44 AM
PauloMorgado wrote:
sgjsakura wrote:
  1. Is there any equivalent design for duplicated placeholders like string.Format("{0} is {0}", x)? The runtime value of the parameter will be computed only once in this mode, but if you using $"{x} is {x}" I believe it will be computed twice.
What makes you think that?
I'm just think about this special case, it does not means I love the duplicated placeholder usage.
Aug 21, 2014 at 8:47 AM
supercat wrote:
PauloMorgado wrote:
What makes you think that?
Having such expressions always be computed twice would be less apt to cause "surprises" than having them be computed once if both occurrences were sufficiently similar, and twice if they differed sufficiently. If one sets down explicit rules as to when an expression will be computed once, and made clear that when those rules do not allow for common subexpression elimination the code must behave as though the expression is evaluated twice, the result might not be too bad, but such rules would add complexity for not much real gain. If one wants to allow expressions to be used more than once in a format string, it would be better to add a "tag" feature, and have what would be repeated invocations of the expression instead be references to the value of the first invocation.
Actually add a tag feature seems to be more complex for the string interpolation design. Just ignore this problem is also acceptable since user can create a temp variable first. What's more, combined with the declare expression feature in C# 6, you may even use $"{var x = ComputedValue()} is {x}" =_=
Aug 21, 2014 at 10:26 AM
supercat wrote:
PauloMorgado wrote:
What makes you think that?
Having such expressions always be computed twice would be less apt to cause "surprises" than having them be computed once if both occurrences were sufficiently similar, and twice if they differed sufficiently. If one sets down explicit rules as to when an expression will be computed once, and made clear that when those rules do not allow for common subexpression elimination the code must behave as though the expression is evaluated twice, the result might not be too bad, but such rules would add complexity for not much real gain. If one wants to allow expressions to be used more than once in a format string, it would be better to add a "tag" feature, and have what would be repeated invocations of the expression instead be references to the value of the first invocation.
Makes sense.
Aug 21, 2014 at 10:27 AM
sgjsakura wrote:
supercat wrote:
PauloMorgado wrote:
What makes you think that?
Having such expressions always be computed twice would be less apt to cause "surprises" than having them be computed once if both occurrences were sufficiently similar, and twice if they differed sufficiently. If one sets down explicit rules as to when an expression will be computed once, and made clear that when those rules do not allow for common subexpression elimination the code must behave as though the expression is evaluated twice, the result might not be too bad, but such rules would add complexity for not much real gain. If one wants to allow expressions to be used more than once in a format string, it would be better to add a "tag" feature, and have what would be repeated invocations of the expression instead be references to the value of the first invocation.
Actually add a tag feature seems to be more complex for the string interpolation design. Just ignore this problem is also acceptable since user can create a temp variable first. What's more, combined with the declare expression feature in C# 6, you may even use $"{var x = ComputedValue()} is {x}" =_=
That would be $"{var x = ComputedValue(); x } is {x}", right?
Aug 21, 2014 at 10:37 AM
Edited Aug 21, 2014 at 10:37 AM
PauloMorgado wrote:
sgjsakura wrote:
supercat wrote:
PauloMorgado wrote:
What makes you think that?
Having such expressions always be computed twice would be less apt to cause "surprises" than having them be computed once if both occurrences were sufficiently similar, and twice if they differed sufficiently. If one sets down explicit rules as to when an expression will be computed once, and made clear that when those rules do not allow for common subexpression elimination the code must behave as though the expression is evaluated twice, the result might not be too bad, but such rules would add complexity for not much real gain. If one wants to allow expressions to be used more than once in a format string, it would be better to add a "tag" feature, and have what would be repeated invocations of the expression instead be references to the value of the first invocation.
Actually add a tag feature seems to be more complex for the string interpolation design. Just ignore this problem is also acceptable since user can create a temp variable first. What's more, combined with the declare expression feature in C# 6, you may even use $"{var x = ComputedValue()} is {x}" =_=
That would be $"{var x = ComputedValue(); x } is {x}", right?
It seems that the declaration expression has its own value, please see the example Console.WriteLine("Result: {0}", (int x = GetValue()) * x); in the C# language feature list https://www.codeplex.com/Download?ProjectName=roslyn&DownloadId=894944 .

Anyway, the detail for declaration expression is not important, I just want to say walkaround is aleady there :-)
Aug 22, 2014 at 12:50 AM
sgjsakura wrote:
It seems that the declaration expression has its own value, please see the example Console.WriteLine("Result: {0}", (int x = GetValue()) * x); in the C# language feature list https://www.codeplex.com/Download?ProjectName=roslyn&DownloadId=894944 .

Anyway, the detail for declaration expression is not important, I just want to say walkaround is aleady there :-)
Right! I was thinking of the semi-colon operator.
Aug 22, 2014 at 11:21 PM
Edited Aug 22, 2014 at 11:32 PM
$"{var x = ComputedValue(); x } is {x}"

Having arbitrary expression in the holes, is starting to look messy and confusing.
  • Are those the same x or is the second coming from an outer scope?
  • Without the aid of syntax highlighting it is just going to (at a quick glace) like a string. Image
    The grey background section indicating the expression (which also has syntax highlighting),
  • If arbitrary expressions are allowed, then it means it can embed another interpolation string inside of the hole of another interpolation string.
$"A$:={$" foo: {foo} bar: {bar}"} B$:={$" { var o = x.IsEven(); return o ?? "Even" : "Odd"; } "; } "  
I don't think that would pass a code review
Aug 23, 2014 at 1:26 AM
Edited Aug 23, 2014 at 1:29 AM
AdamSpeight2008 wrote:
Having arbitrary expression in the holes, is starting to look messy and confusing.
Agreed. If this is the direction that it's going to go, then I'd rather not have it all. If I could imagine one thing worse and less maintainable, than existing usages of string.Format with 10 holes, it's pushing all those expressions into the string. The string literal should express the goal. How you get to the goal should be seperated out and not embedded in the string. That was the reason for my previous proposal.

I understand the goal is to make breaking in and out of the string "asdf" + something.Blah + "asda" simpler, but when you have such string concatenation so complex that you feel it needs to be simplified, your not buying yourself much just by eliminating a few "+" pairs. You still have a mess of expressions interupting the string. You've taken something bad and made it slightly less bad, but it is still bad. The opposite end of the sprectrum is string.Format, which pulls everything out, but the numbered holes require you to count through parameters to mentally map which express goes with which hole. IMO string.Format is a heck of alot cleaner than concatenation, we just need to convert numbered holes into named holes, which is exactly what I was proposing.

Otherwise you're beginning to venture into territory pretty well covered by templating engines already. For what it buys us, I don't think it's worth the complexity added to the language and compiler support. You also run into alot of confusing edge cases. For example, Razor at its essence is a string templating engine. It has some specialization in HTML, but it can be adapted to be used for non HTML string generation. If you've ever needed to conditionally output a class nested inside a another string you end up with some really fun to read stuff.
string someResult = $"some string stuff $(someThing > 1 ? "nested string result of an ternary" : "we are in a nested expression context so anyone reading must understand these quotes apply to the expression, not the outer string")";
Aug 23, 2014 at 1:31 AM
Edited Aug 23, 2014 at 1:33 AM
What's more, if you allowed that, then this is equally valid but exponentially horrible:
string someResult = $"some string stuff $(someThing > 1 ? "nested string result of an ternary" : $"we are in a nested $(numberOfExpressions > 1 ? "expressions": "expression") context so anyone reading $(hasUnderstanding ? "understands" : "does not understand") these quotes apply to the expression, not the outer string")";
Aug 23, 2014 at 2:17 AM
AaronLS wrote:
What's more, if you allowed that, then this is equally valid but exponentially horrible:
string someResult = $"some string stuff $(someThing > 1 ? "nested string result of an ternary" : $"we are in a nested $(numberOfExpressions > 1 ? "expressions": "expression") context so anyone reading $(hasUnderstanding ? "understands" : "does not understand") these quotes apply to the expression, not the outer string")";
We're not in a bubble. Plenty of other languages offer this functionality today and we can examine exactly what happens and how that language has decided to mitigate it without trotting out arbitrary strawmen. The fact that it can be horribly abused isn't a reason to not permit a feature. The question is whether developers might accidentally abuse it when intending to accomplish something else, like with switch-case fall through and the like.

In this case, for Swift it appears that Apple has decided that an interpolation expression cannot contain another string literal, interpolated or not. The following is legal:
let b = true;
let x = "World";
let y = "Dolly";
let s = "Hello \(b ? x : y)!";
While the following is not:
let b = true;
let s = "Hello \(b ? "World" : "Dolly")";
I'd probably agree that it would be stepping too far to permit an interpolated string as an expression within an interpolated string. The decision appears to start at simple member expressions, which would cover the common cases, and explore outwards from there, which seems appropriate.

I'm not completely for about ES6 but it does appear that Traceur does permit embedding template strings within template strings and your example is possible, which might be what you were thinking about considering you mixed in ES6's hole syntax instead of C#'s.
Aug 25, 2014 at 3:54 PM
Edited Aug 25, 2014 at 6:05 PM
Halo_Four wrote:
"we can examine exactly what happens and how that language has decided to mitigate it without trotting out arbitrary strawmen."
1) How do you get to point C where you are looking at mitigating a problem without first visiting B where you acknowledge a potential problem? I can't believe you're actually saying you can evaluate how to mitigate a problem without first acknowledging the problem! How do you do that? That's liek saying "We can cure disease X without having actually disovered that disease X exists." Your argument is to say we should do no use case evaluation and you base that argument on the basis that other languages have made the same mistake and have to mitigate it. Let's do away with properties too while we're at it, and go back to using getSomething()/setSomething() like java does if that is a logical argument.

2) Comparing use cases is not an arbitrary strawman. It was meant as a comparison against previous proposals which allow for such nesting in a much more clean and maintainable way. If you can accomplish such a complex case without the complexity of the code exploding geometrically then why ignore it?

"The fact that it can be horribly abused isn't a reason to not permit a feature."

3) True. Should we disallow a feature simply because it can be abused? No. What if it is EASY to abuse? Then we should consider an alternative implementation. It is absolutely a reason to consider an alternative and equally valuable feature. C# encourages a pit of quality. If you can provide a feature that accomplishes the same goals and encourages a pit of quality then why choose an implementation that doesn't? If one feature makes it difficult to write great code and easy to write terrible code, and another makes it easy then the former is better.

Anyone who tries to accompolish similar nesting with the named holes approach when they aren't allowed to use expressions, will then make the next logical stab at it:
string someResult = $("Now welcoming {fullTitle}", 
  fullTitle : $("Sir Robin of {origin}", origin: isManInTights ? "Sherwood Forest" : "Knottingham" )
 );
Accomplishes the same thing and the nesting can be repeated. Each string is easy to read and the named holes express the sementics: "Now welcoming {fullTitle}" "Sir Robin of {origin}". It is always better to for a language to express the "What" and abstract away the "How". We haven't pushed the "How", the expression far away, but just enough to significantly improve readability. You can still easily follow how the holes are being generated. Now you don't have to implement intellisense inside string expressions! The worst you have to do compile-time is look for named holes and verify they match the named parameters. Imagine intellisense inside a string. How many times is it going to activate when you aren't really in a hole and it decides you typing "I " should insert some keyword that starts with I. Ever used SQL intellisense in SSMS and had it randomly replace your attempts to type an alias with some random keyword?

You can keep nesting and the complexity of the code doesn't explode geometrically.

To do the same cleanly when expressions are allowed requires self-discipline, and then you have to declare explaining variables to pull the expressions and nested string out, and build the holes before the actual goal strings. So you end up doing it all backwards:
string origin = (isManInTights ? "Sherwood Forest" : "Knottingham");
string fullTitle = $("Sir Robin of {origin}");
string someResult = $("Now welcoming {fullTitle}", 
Granted, I have exaggerated here because it'd probably not be offensive to have inlined {origin} with the expression. But it certainly is nicer when all holes are named and improves the semantic readability of the string. I can very quickly see what the hole represents and not care how it is composed if when I'm reading the code. That's the What instead of the How.

I'm so tired of seeing string.Format with 10 to 20 parameters at every job I have ever worked at in the last 10 years, and having to count through them everytime I have to debug their code. If you allow expressions inside strings then you are going to make it EASIER to write worse code. That's the opposite of a pit of quality.
Aug 25, 2014 at 5:05 PM
AaronLS
The worst you have to do compile-time is look for named holes and verify they match the named parameters.
It already partial implemented in String.Format diagnostic. Just need extending to parse identifiers (which implemented already somewhere in the Roslyn source).
And you still be able to include alignment and format args as well.
Any one want to fork it implement it?

I said before that there some be a KeyValuePair Literal / operator := so it doesn't clash with label syntax. inline-if else and string formatting.
var dic = { "A" := 1 , "B" := 2, "C" := 3 } /* A Collection of KeyValuePair <String. Int>
AaronLS's example
string someResult = $("Now welcoming {fullTitle}", fullTitle := $("Sir Robin of {origin}", origin:= isManInTights ? "Sherwood Forest" : "Knottingham" ) );
Scoping Rules

Current the String.Format index the parameter array with the string format. Should interpolated string be the same?
  1. Must Local first. Within the parenthesis of the interpolated string.
  2. Next Higher scope
  3. etc.
  4. Not found? UnknownNamedHoleException
Case-Sensitive in C#, Case-Insensitive in VB.net.
Aug 25, 2014 at 7:38 PM
Edited Aug 25, 2014 at 7:40 PM
AdamSpeight2008 wrote:
I said before that there some be a KeyValuePair Literal / operator := so it doesn't clash with label syntax. inline-if else and string formatting.
Yes, I certainly don't intend my example to be a promotion of a particular syntax. Merely to show how clean your string literals are with the general named hole approach. There are alot of directions you could go with this and maintain the general principal of that proposed approach. Another possibility is using anonymous objects, which would be similar to how dotliquid's templating works. I personally think it would be nice if we minimized the amount of new syntax/operators. It could be done purely with existing syntax as a runtime API/feature, but probably wouldn't perform as well. Either way, I agree you certainly would want the diagnostics you mention to validate names at compile time, which could be leveraged in either case whether it was a runtime or compile time feature. In the former case you'd have to make the diagnostic ignore non-string literals since if it is a runtime feature then someone could be passing a dynamic string the same way you can use string.Format. Sometimes you have a string as a resource or template from a DB and you have to do the hole filling at runtime.
string someResult = string.Fill("Now welcoming {fullTitle}", new { 
    fullTitle = string.Fill("Sir Robin of {origin}", new {origin = isManInTights ? "Sherwood Forest" : "Knottingham"} )
  });
I am starting to rehash discussions from couple weeks ago a bit. I'll just say there are some convincing arguments both for and against it only applying to string literals at compile time. I think I would be happy to have it either way. I simply feel strongly that experssions inside the holes should not be allowed. And also, some what less strongly, feel that names should only refer to explicit fields declared in the named parameters(or whatever syntax) rather than allowing other things like local variables, properties, and parameter names. Potential risks and alot of things like find all references and refactoring any of these names would not discover the references in the named holes(unless all of these toolings also being parsing string literals). Plus there is a potential risk in allowing holes to directly reference any identifier in current scope, I admit though, this risk is less of a concern when we are talking about string literals.

Forcing named holes solves the references/refactoring problem since new { holeName= someParameter, anotherHoleName = someVariable } ensures refactorings of someParameter or someVariable will continue to work as they do today, and won't require all of these various tools that do that kind of stuff to now implement parsing string literals to check if named holes reference a current scope variable/param/property.
Aug 25, 2014 at 8:16 PM
AaronLS wrote:
Halo_Four wrote:
"we can examine exactly what happens and how that language has decided to mitigate it without trotting out arbitrary strawmen."
1) How do you get to point C where you are looking at mitigating a problem without first visiting B where you acknowledge a potential problem? I can't believe you're actually saying you can evaluate how to mitigate a problem without first acknowledging the problem! How do you do that? That's liek saying "We can cure disease X without having actually disovered that disease X exists." Your argument is to say we should do no use case evaluation and you base that argument on the basis that other languages have made the same mistake and have to mitigate it. Let's do away with properties too while we're at it, and go back to using getSomething()/setSomething() like java does if that is a logical argument.

2) Comparing use cases is not an arbitrary strawman. It was meant as a comparison against previous proposals which allow for such nesting in a much more clean and maintainable way. If you can accomplish such a complex case without the complexity of the code exploding geometrically then why ignore it?
No, my argument is quite the opposite. It is that since there are a number of existing languages that implement this feature that we can use the actual data about the use cases to make appropriate design decisions rather than identifying the most complicated of edge cases and using that to argue against the proposed feature or to propose alternate syntax that would specifically address those use cases.
I think I would be happy to have it either way. I simply feel strongly that experssions inside the holes should not be allowed. And also, some what less strongly, feel that names should only refer to explicit fields declared in the named parameters(or whatever syntax) rather than allowing other things like local variables, properties, and parameter names. Potential risks and alot of things like find all references and refactoring any of these names would not discover the references in the named holes(unless all of these toolings also being parsing string literals). Plus there is a potential risk in allowing holes to directly reference any identifier in current scope, I admit though, this risk is less of a concern when we are talking about string literals.

Forcing named holes solves the references/refactoring problem since new { holeName= someParameter, anotherHoleName = someVariable } ensures refactorings of someParameter or someVariable will continue to work as they do today, and won't require all of these various tools that do that kind of stuff to now implement parsing string literals to check if named holes reference a current scope variable/param/property.
You keep forgetting that these are not string literals. They are string templates and the compiler is fully aware of the holes and their contained expressions. The Roslyn parsers have done the difficult work and any tool built on that API will get refactoring capabilities out-of-the-box.

I agree that embedding arbitrary expressions is a problem from a perspective of complexity and parsing ambiguity, particularly since the C# team intends to support composite formatting modifiers. The C# team seems to think that starting at locals and simple member access would be safe and I am inclined to agree with them. I do think that extended support for basic arithmetic, numeric indexers and simple method calls are probably just fine. I do like your idea of additional named holes which can be filled using a named argument syntax and think that could be used to expand the support to additional expressions, but that would be supplementary to the normal interpolation syntax and at the end of the day it still generates a normal call to string.Format.
Aug 25, 2014 at 8:59 PM
Edited Aug 25, 2014 at 9:01 PM
You keep forgetting that these are not string literals. They are string templates and the compiler is fully aware of the holes and their contained expressions. The Roslyn parsers have done the difficult work and any tool built on that API will get refactoring capabilities out-of-the-box.
As an example it could have a similar construction like the following

base IStr

IStr_Text  <: IStr
  .Text : String!

IStr_Hole  <: IStr
  .Alignment : IAlignmentArg
  .Format    : IFormatArg

IStr_IndexHole
  .Index     : Int32

IStr_NamedHole <: IStr_Hole
  .Name      : String!

Base InterpolatedParam
  .Content : Object

IndexedParam <: InterpolatedParam
  .Index : Int32
NamedParam  <: InterpolatedParam
  .Name : String!

InterpolatedString
   .Parts : List<IStr>
   .Params : List<InterpolatedParam>
   .ToString() : String
   {
     var sb = new StringBuilder()
     foreach( part In parts )
     {
       match( typeof( part ) ) with
       {
         | IStr_Text As x => sb.Append( x.Text )
         | IStr_IndexHole As ih => sb.AppendFormat( params[ih.Index], ih.Alignment, ih.format)
         | IStr_NamedHole As nh => sb.AppendFormat( params[nh.Name ], nh.Alignment. nh.format)
       }
       return sb.ToString()
     }
   }
Obviously with improved handling of errors.
Aug 25, 2014 at 10:42 PM
For me, any solution must:
  • improve on plain old concatenation in terms of keystrokes and readability;
  • allow arbitrarily complex expressions;
  • allow for localization; and
  • perform well
  • avoid expressions embedded in strings, which are an anti-pattern IMHO (regardless which other language allows them).
It seems there are two primary camps, and I'm not opposed to either, if the implementation meets the above criteria:

Camp 1: Literal and inline variable bits interspersed.

This is basically how plain old string concatenation works now, what is being debated is an alternative syntax.

I don't believe that sticking expressions inside the string is going to improve readability, parsing, or keystrokes at all. This expression-in-a-brace-in-a-string thing is overwrought and over-engineered, and the only excuse for it being "good" seems to be that other languages have similar Inception-style string-based expressions.

The best way to improve concatenation of literals and arbitrary inline expressions is to improve the syntax for string concatenation, not re-invent the wheel within another wheel. I'm open to suggestions, but so far the best solution to me is to extend C# to allow postfix notation of the "+" operator. This removes all of the superfluous "+" symbols, keeps literals and expressions contextually separate, and doesn't completely reinvent the wheel for interpreting expressions.

White space is optional between the terms, and having the option to have line breaks in particular is useful for complex expressions. Color-coding would easily show the literal and variable bits, as it does now with concatenation.

Example:
string someResult = "Thank you " name " for ordering " cart.Count 
    " product" (cart.Count>1 ? "s" : "") " from our store"+;
Camp 2: Named placeholders for the variable bits, paired with expressions defining the names.

This is basically String.Format, with named variables. Having explicit names and separate definitions is a Good Thing(TM) for clarifying intent, which is an advantage over normal string concatenation or other forms of inline expressions. Whether that advantage merits the potential down side of having to visually match the named variables with their definitions is highly subjective, and can also depend on the use case. It's certainly at least better than the current String.Format syntax (index numbers), and the more complex a variable's expression is, the more likely having a named reference will be preferable to having the expression inline.

For this approach, I'm still a fan of extending C# to support an arbitrary number of named parameters ("params on steroids"):
string someResult = String.Format("Thank you {name} for ordering {count} product{pluralizeCartCount} from our store",
    name: name,
    count: cart.Count,
    pluralizeCartCount: cart.Count > 1 ? "s" : ""
);
The precise syntax could be done different ways and could take advantage of C#'s upcoming feature for establishing key/value pairs, but overall this approach provides a good separation of the variable placeholders and their definitions without an overload of extra fluff to, say, instantiate and populate a dictionary. And, unlike the first solution, the first argument of String.Format would not need to be a literal, thus allowing localization.

If I had to choose between the two approaches, I would pick the second.
Aug 25, 2014 at 11:55 PM
richardtallent wrote:
It seems there are two primary camps, and I'm not opposed to either, if the implementation meets the above criteria:

Camp 1: Literal and inline variable bits interspersed.
string someResult = "Thank you " name " for ordering " cart.Count 
    " product" (cart.Count>1 ? "s" : "") " from our store"+;
Camp 2: Named placeholders for the variable bits, paired with expressions defining the names.
string someResult = String.Format("Thank you {name} for ordering {count} product{pluralizeCartCount} from our store",
    name: name,
    count: cart.Count,
    pluralizeCartCount: cart.Count > 1 ? "s" : ""
);
You missed the third camp: proper compile-time string interpolation. It's already been decided as being a fancy syntax on top of string.Format converted at compile-time. It seems that the only question remaining by the C# team was the extent of the expressions permitted within the holes.
string s = $"Hello {person.Name}, you were born on {person.BirthDate:G}.";
Aug 26, 2014 at 12:23 AM
Edited Aug 26, 2014 at 12:30 AM
richardtallent
So your advocating / of the opinion that the named holes should restrict scoping to within the bounds of the String.Format( )
String.Format( " { 0,-3 } {Song_Name, 32} [Artist,32} ", Index, Song_Name; nameofsong, Artist: performer )

If "we" extend the scoping out a bit further, it would be possible to just write the following.
var TrackListing = from CD in Music
                   from track in CD.Tracks
                   select $("{track.Number,-3): {track.Song_Name. 32} {track.Artist. 32}")
Since we can parse the interpolation string into parts. extending both the type-checker an the syntax highlighting to the inside would back things clearer.
Especially if semantic highlighting is used. ```track.`` would be the same color so you could see the flow of the data.

var s = $("{$track.Number,3}: {1, 32} {trackArtist,32)", trackArtist:= track.Artist, track.Song_Name)
Hole
  1. An extended scoped named hole ( akin to current variable scoping rules )
    aka Identified Hole (refers to an Identifier)
  2. An Indexed Hole
  3. Named Hole
Arbitrary Expression are confined to the Indexed Hole and Named Hole paramaters.
Aug 26, 2014 at 1:18 AM
Halo_Four wrote:
You missed the third camp: proper compile-time string interpolation.
If you put a placeholder at the position in the string where you want the evaluated result to occur, it's Camp 2. If you the expression to be evaluated there, it's Camp 1.

So your example falls within Camp 1, you're just using different syntax and implementation method than I'm proposing.

I'm proposing a tweak to reduce the syntactic overhead of the binary "+" operator on string concatenation as an alternative to the $"" approach.

Both put the expressions positionally within the string at the insertion point -- mine just does so without having to write a whole new template language and creating oddball issues like escaping curly brace characters.
It seems that the only question remaining by the C# team was the extent of the expressions permitted within the holes.
Yes, if only placeholders are allowed, it would be a Camp 2 approach, but if expressions are allowed (referencing in-scope variables, adding logic, etc.), it's Camp 1.

Here's your example, and my own preference for a Camp 1 solution:
string s = $"Hello {person.Name}, you were born on {person.BirthDate}.";
string s = "Hello "person.Name", you were born on "person.BirthDate"."+;
I did remove the explicit string format to illustrate the syntax similarity better... I'll grant that a template language can handle ToString() in fewer characters, but it wouldn't be able to use a variable format, such as the user's locale or an application setting.

One advantage to my example is that when the expressions get more complicated than accessing property, you can add line breaks or parenthesis sets to make the logic clear:
string s = "Hello "person.Name", you were"
    (person.BirthDate < DateTime.Today.AddDays(-1) ? " not" : "")
    " born yesterday.";
Aug 26, 2014 at 2:00 AM
AdamSpeight2008 wrote:
So your advocating / of the opinion that the named holes should restrict scoping to within the bounds of the String.Format( )
Yes, just like {0} has no meaning to anything but the String.Format method now.
String.Format( " { 0,-3 } {Song_Name, 32} [Artist,32} ", Index, Song_Name; nameofsong, Artist: performer )
I think for a given call you'd have to either do indexed positions (0, 1,...) or named (Song_Name), not mix and match them, so the overload that uses params[] or the named parameter equivalent is called, but yes.
If "we" extend the scoping out a bit further, it would be possible to just write the following.
var TrackListing = from CD in Music from track in CD.Tracks select $("{track.Number,-3): {track.Song_Name. 32} {track.Artist. 32}")
Interesting use case.

Let me assume for a minute that we use "$" as a simple shortcut for String.Format. I think a cleaner approach would be (whitespace optional of course):
var TrackListing = from CD in Music from track in CD.Tracks select $("{num,-3): {song,32} {artist,32}", 
        num: track.Number,
        song: track.Song_Name,
        artist: track.Artist
);
Aug 26, 2014 at 2:03 AM
richardtallent wrote:
Halo_Four wrote:
You missed the third camp: proper compile-time string interpolation.
If you put a placeholder at the position in the string where you want the evaluated result to occur, it's Camp 2. If you the expression to be evaluated there, it's Camp 1.

So your example falls within Camp 1, you're just using different syntax and implementation method than I'm proposing.
Concatenation could be one implementation of string interpolation, beneath the hood anyway. I think that getting RPN into the compiler would be a non-starter. The C# team already considered concatenation syntax in the first post of this thread, including with simplification of syntax, and dismissed it.

I still refer to the May 27th C# team design notes where the syntax was nailed down to the syntax that I used in my example. I do hope that they would weigh in on the subject in one way or another in an upcoming meeting just to give these conversations something more concrete to concentrate on.

I do like elements of AaronLS's named placeholder syntax as a way to expand upon the syntax particularly where embedded expressions would be unwieldy even though it is only a minor difference from declaring a temporary variable and interpolating that.
Aug 26, 2014 at 2:32 AM
Halo_Four wrote:
I think that getting RPN into the compiler would be a non-starter.
Alas.
I do like elements of AaronLS's named placeholder syntax as a way to expand upon the syntax
Me too, and I was hoping the ship hadn't totally sailed. I'm just not a fan of expressions embedded within strings. I'm far more comfortable with named placeholders.

I think of it in MVC-like terms -- using named holes give you clean separation between the view (the string template), the model (the key/value collection of named parameters), and controller (the expressions that determine said keys' values).

In terms of implementation of named parameters, you could extend params to support named arguments (itself useful in other contexts), or you could simply do the same sort of compile-time rewrites to traditional String.Format (with indexed holes) or to plain old concatenation as the current solution proposes.
Aug 26, 2014 at 8:51 PM
Edited Aug 27, 2014 at 11:36 AM
Interpolation is some kind of an operation or? Why not to use an operator for doing it? ":"

Operation could be checked at compile time.
var x = new
{
    World = "World",
    Count = 42,
}

"Hello {World}. What is the Truth? {Count}!" : x
Or
"Hello {World}. What is the Truth? {Count}!" : new { World = "World", Count = 42 }
Or
"Hello {World}. What is the Truth? {Count}!, {Foo}" : str => new { World = "World", Count = 42, Foo = str.Length }
Or
"Hello {World}. What is the Truth? {Count}!, {Foo}" : str => new []{ "World", 42, str.Length }
Or
"Hello {0}. What is the Truth? {1}!, {2}" : str => new []{ "World", 42, str.Length }
Or all togehter ;)
Aug 27, 2014 at 5:39 AM
A Interpolated String consists of two basic classifications. Text and Arg (Hole).
MustInherit Class FormatStringBlock
  Overridable Function SourceText() As String
  End Function
End Class

NonInheritable Class Text
  Inherits FormatStringBlock
End Class

MustInherit Class Arg
  Inherits FormatStringBlock
  ReadOnly Property Alignment As FormatStringAlignment
  ReadOnly Property Formatting As FormatStringFormatting
End Class
Let's say that the arg (hole) could be one of three forms.
  1. Indexed { 0 }
  2. Named { name }
  3. Identified {$id}
NonInheritable Class Indexed 
  Inherits Arg
  Public ReadOnly Property Index() As Integer
End Class

NonInheritable Class Named
  Inherits Arg
  Public ReadOnly Property Name() As String!
End Class

NonInheritable Class Identified
  Inherits Arg
  Public ReadOnly Property Identifier() As Identifier
End Class
The parameter list (potentially) supplied is not as simple like it is for String.Format since it now there is the possibility of it being a named parameter.
Class FormatStringArg
  ReadOnly Property Value() As Object
End Class
Class NamedArg
  Inherits FormatStringArg
  ReadOnly Property Name() As String
End Class
So let's now implement the function .ToSting() that output the result of an interpolated string.
Class InterpolatedString

  Dim Blocks As New List(Of Block)
  Dim _Args As New List(Of FormatStringArg)
  
  Const _ArgCountLimit = 1000000

  Function ToString() As String
    Dim _NamedArgs = Args.OfType(NamedArg)
    Dim sb As New Text.StringBuilder
    ForEach block In Blocks
      Select Case TypeOf(block)
        Case Text    As tb :  sb.Append(tb.Text)
        Case Indexed As ib 
          Dim InRange = ib.Index.IsBetween(0,ib.Count)
          Select Case InRange
            Case True : sb.Append( Args( ib ).Value )
            Case False When ib.Index >= ArgCountLimit : Throw Exceptions.ArgCountLimitExceed()
            Case Else : Throw Exceptions.IndexOutOfRange()
          End Select
        Case Named As nb
          Dim d = _NamedArgs.Where(Function(b) b.Name = nb.Name)
          If Not d.Any() Then   Throw Exceptions.NamedArgNotFound()
          Dim res = d.First
          sb.AppendFormat(res.Value, block.Alignment, block.Formatting
        Case Identified As Id 
           Dim res = Compilier.FindIdentifier(Id.Identifier)
           If res Is Nothing Then Throw Exceptions.IdentifierNotFound()
           sb.AppendFormat( res.Value, block.Alignment, block.Formatting )
      End Select
    Next
    Return sb.ToString()
  End Function

End Class
It is relatively simple to implement, the only complex part is the locating of Identifiers. But the compiler only has support for that, it just a matter of accessing that functionality. I haven't implemented the parsing of an interpolated string (yet) because that aspect. it should be that hard to extend my current implementation to parse named and identified arg (holes).
Aug 27, 2014 at 12:43 PM
Edited Aug 27, 2014 at 12:44 PM
JavedSaqib wrote:
Interpolation is some kind of an operation or? Why not to use an operator for doing it? ":"

Operation could be checked at compile time.
var x = new
{
    World = "World",
    Count = 42,
}

"Hello {World}. What is the Truth? {Count}!" : x
Or
"Hello {World}. What is the Truth? {Count}!" : new { World = "World", Count = 42 }
It could be also somthing like a object intilizer (in this case special string intilaizier) for compile time.
For runtime the operator would be sufficient.
"Hello {World}. What is the Truth? {Count}!"
{
    World = "World",
    Count = 42,
}
Operation with the string on the right side, could lead to infer the type and give intellisense for the string.
x : "Hello {World}. What is the Truth? {Count}!"
One important thing should be, that it should be possible to interpolate type safe a interpolated string.

Example:
var str = "Hello {World}. What is the Truth? {Count}!"
{
    World = "World",
    Count = "42",
}

str : new { World = "New World" } // correct
str : new { Worldx = "New World" } // compile time error, because interpolated string does not match the placeholder metadata.
So for dynmaic string you must convert/cast into interpolated string.
var stringFromDatabase = "Some string with some placeholders {PLACEHOLDER1} {PLACEHOLDER2}";

var interStr = stringFromDatabase << new []{ PLACEHOLDER1 = typeof(String), PLACEHOLDER2 = (typeof(Char))}
Or infer from type
var myType = new
{
    PLACEHOLDER1 = "Some Text",
    PLACEHOLDER2 = 'X',
};

var interStr = stringFromDatabase << myType 
Or an interpolated string or a string which should be interpolated can be seen as a type with extended fields/poperties

Example:
var someString = "Some Text {A}, {B}";

someString.A = "Hello";
someString.B = "World";
Some one agrees with that? I think it is more natural for C# (maybe VB).
Aug 27, 2014 at 5:06 PM
Edited Aug 31, 2014 at 11:23 PM
have a nearly complete implementation of a parser.for an interpolated string of the form $(" ",)
See a previous post for rough grammer.
It just require the.functions that parses Identified and Named hole completing.
Will post when internet connect is.better (when the fix it)

Edit

I opened a discussion topic over on String.Format Diagnostic which contain the core function.

Parse Interpolated String
Aug 31, 2014 at 11:13 PM
The is an interesting possible of the above format of string interpolation:- template function
Oct 6, 2014 at 5:12 PM
Edited Oct 6, 2014 at 5:13 PM
I agree that some improvements over ugliest string.Format were done (BTW, who architected that "Format"? Is he still alive?). But why again we see all those backslashes? Are you still sleeping in C-language era?
var a = `My name is {name}`
  • isn't it enough?? (with backticks as string limiter and just curlies for substitution)
Oct 6, 2014 at 5:57 PM
Edited Oct 6, 2014 at 5:59 PM
I find 'dapper style' string interpolation (with dynamic data type) awesome. Absolutely understand {0}..{N} haters, string names are more useful, but don't think it must be some new language feature. Just another overload .Format with another style of format string would be ok.
Kinda:
string a = "hello world";
Console.WriteLine(
    string.Format(
        "'{A}'.Count = {B}",
        new{
            A = a,
            B = a.Length
        }
    )
);
Or, with http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2406863-make-it-possible-to-call-string-format-like-my :
Console.WriteLine(
    "{C:G}: '{A}'.Count = {B}".Format(
        new{
            A = a,
            B = a.Length,
            C = DateTime.Now
        }
    )
);
Oct 6, 2014 at 6:03 PM
Vincent3000 wrote:
I agree that some improvements over ugliest string.Format were done (BTW, who architected that "Format"? Is he still alive?). But why again we see all those backslashes? Are you still sleeping in C-language era?
var a = `My name is {name}`
  • isn't it enough?? (with backticks as string limiter and just curlies for substitution)
These meeting notes are obsolete. The conclusion during the May meeting was to use a prefix on the string literal to indicate that it is an interpolated string, e.g.
string result = $"Hello, my name is {name} and I was born on {dob:D}.";
Which would be translated to the following:
string result = String.Format("Hello, my name is {0} and I was born on {1:D}.", name, dob);
As for the backslash syntax, that was actually what Apple Swift had chosen to use to escape the "holes" in the interpolated string. It is appropriate for a language of c heritage as C# is.
Oct 6, 2014 at 6:15 PM
agat50 wrote:
I find 'dapper style' string interpolation (with dynamic data type) awesome. Absolutely understand {0}..{N} haters, string names are more useful, but don't think it must be some new language feature. Just another overload .Format with another style of format string would be ok.
Kinda:
string a = "hello world";
Console.WriteLine(
    string.Format(
        "'{A}'.Count = {B}",
        new{
            A = a,
            B = a.Length
        }
    )
);
I agree, but it's not the plan... I think this approach is better because it enables localization and is consistent with the existing String.Format (actually you can already have this today with library support).
Baking it into the language does have a few benefits, though:
  • Better performance (because there is no need to use reflection to get the values)
  • Complex expressions in the placeholders, rather than just simple names
  • Compiler checking of the expression
Coordinator
Oct 6, 2014 at 6:15 PM
Halo_Four wrote:
These meeting notes are obsolete. The conclusion during the May meeting was to use a prefix on the string literal to indicate that it is an interpolated string, e.g. $"hello {customer.name}"
We had a follow-up meeting in September to revisit string interpolation, and played around with how everything feels, and actually came to prefer "hello \{customer.name}" for C#, but still $"hello {customer.name}" for VB. That's what we've implemented so far in prototype form. It won't be out in CTP4, but it will likely be out in the release after that for C# (and a little later for VB).

When it does come out, we'll want people to play around with it in practice to see how it feels. And for people to play with it in practice before voicing further strong opinions :)

The scenario the switched our minds was the typing process, e.g. I type var x = "hello | and at this point I realize I want an interpolated string. In one design I have to cursor back to the start of the string and insert a $. In the other design I can continue typing right where I am.

-- Lucian from VB/C# LDM
Oct 6, 2014 at 6:25 PM
@lwischik
Why can't they share the same syntax / style?
Oct 6, 2014 at 6:26 PM
lwischik wrote:
Halo_Four wrote:
These meeting notes are obsolete. The conclusion during the May meeting was to use a prefix on the string literal to indicate that it is an interpolated string, e.g. $"hello {customer.name}"
We had a follow-up meeting in September to revisit string interpolation, and played around with how everything feels, and actually came to prefer "hello \{customer.name}" for C#, but still $"hello {customer.name}" for VB. That's what we've implemented so far in prototype form. It won't be out in CTP4, but it will likely be out in the release after that for C# (and a little later for VB).

When it does come out, we'll want people to play around with it in practice to see how it feels. And for people to play with it in practice before voicing further strong opinions :)

The scenario the switched our minds was the typing process, e.g. I type var x = "hello | and at this point I realize I want an interpolated string. In one design I have to cursor back to the start of the string and insert a $. In the other design I can continue typing right where I am.

-- Lucian from VB/C# LDM
You could ship Roslyn with a money-printing printer and people would voice "strong opinions" regarding that they still had to load the thing with paper.

Interesting on the redesign. I didn't see it mentioned in the September meeting notes. Honestly either way works for me, although I'm curious as to how the function prefixing (e.g., loc$"") might come into play, if it will at all.
Oct 6, 2014 at 6:32 PM
Edited Oct 6, 2014 at 6:38 PM
What if you want the characters { and } in your interpolated string? Especially the c# version. As \{ would suggest that but from the example it doesn't.

In the VB version it is simply {{ and }} respectively

Ctrl + Left Arrow also helps when going to the beginning of the string. Maybe an additional shortcut Ctrl + Alt + Left Arrow? to jump to the start?
Oct 6, 2014 at 6:41 PM
Edited Oct 6, 2014 at 6:42 PM
@Halo_Four $ would have to be both a prefix and also a binary operator like -

Which would be funky as in VB it also is a type identifier for string. Dim mystring$
Oct 6, 2014 at 6:41 PM
lwischik wrote:
The scenario the switched our minds was the typing process, e.g. I type var x = "hello | and at this point I realize I want an interpolated string. In one design I have to cursor back to the start of the string and insert a $. In the other design I can continue typing right where I am.
That's a good point, but I think I still prefer the "$" prefix... Will this syntax still allow the use of a custom interpolator function?
Oct 6, 2014 at 6:55 PM
I agree, compile check is good (despite on it's quite hard to implement full code edit in such expressions in IDE imho). Actually, i'm more concerned about one thing - lets face it, real var names are often long and ugly (not to mention complex expressions). Format string will transform rather to code than format. Don't think it'll be easy to read. {0}...{N} are short, and it's real advantage. Anyway, seemingly it's more about user-friendly ide supporting.
Oct 6, 2014 at 6:58 PM
AdamSpeight2008 wrote:
What if you want the characters { and } in your interpolated string? Especially the c# version. As \{ would suggest that but from the example it doesn't.

In the VB version it is simply {{ and }} respectively

Ctrl + Left Arrow also helps when going to the beginning of the string. Maybe an additional shortcut Ctrl + Alt + Left Arrow? to jump to the start?
You wouldn't be required to escape the curly braces as they would be considered content and not a part of the interpolation, e.g.
string s = "The coords of the point are:  { \{p.x}, \{p.y} }.";
would be converted to:
string s = String.Format("The coords of the point are {{ {0}, {1} }}.", p.x, p.y);
Developer
Oct 6, 2014 at 7:00 PM
AdamSpeight2008 wrote:
What if you want the characters { and } in your interpolated string? Especially the c# version. As \{ would suggest that but from the example it doesn't.
If you want { in your string you type {.

If you want } in your string you type }.

What could be simpler?
Oct 6, 2014 at 7:16 PM
@nmgafter
It kinda breaks this where \\ indicates that you want to use character \ in the string. \{ It also moves C# and VB.net strings even further apart.

You can't tell if the user actually meant that string "The coords of the point are: { \{p.x}, \{p.y} }." or they mean interpolated string.
The use of prefix helps to differentiate the two cases.
Oct 6, 2014 at 7:31 PM
AdamSpeight2008 wrote:
@nmgafter
It kinda breaks this where \\ indicates that you want to use character \ in the string. \{ It also moves C# and VB.net strings even further apart.
That is standard and expected behavior for any c-heritage language going back over 40 years. The \ is the important part of the escape sequence. Notably, \{ is currently illegal syntax so to co-opt it for interpolation cannot break any existing code. The difference here with VB isn't that important because BASIC's heritage does not have escape sequences in strings, save for the doubling of double-quotes in order to insert a single double quote.
You can't tell if the user actually meant that string "The coords of the point are: { \{p.x}, \{p.y} }." or they mean interpolated string.
The use of prefix helps to differentiate the two cases.
Depends on where you're looking. If you came across {p.x} in the middle of a normal string you'd have to glance back to the beginning to figure out if it were intended to be interpolated or just a part of the literal. But if your goal is to know whether the string is interpolated or not then a prefix would help in determining that quickly. Either way I think syntax coloring in the IDE would be the most useful tool in visually interpreting the string.

My opinion isn't that strong one way or the other. I originally favored prefixing over escaping but after toying with Apple Swift I think that escaping feels as natural.
Oct 6, 2014 at 7:41 PM
You shouldn't have rely on syntax highlighting to determine the meaning.
That is standard and expected behaviour for any c-heritage language going back over 40 years.
May be that's the problem. C# isn't C.
Oct 6, 2014 at 7:56 PM
AdamSpeight2008 wrote:
You shouldn't have rely on syntax highlighting to determine the meaning.
And you wouldn't need to, but it'll be more obvious. Otherwise you'd have to scan somewhere else to see what the programmer's intent was, whether the syntax was a prefix or an escape sequence. In my opinion I think it would be more common for a programming to be scanning the contents and to see an expression hole and to want to know whether or not that represented an interpolation rather than to want to know this without having read any of the string.
That is standard and expected behaviour for any c-heritage language going back over 40 years.
May be that's the problem. C# isn't C.
That it's not, but its syntax is deliberately based on that of C, including escape sequences.
Oct 6, 2014 at 8:49 PM
Edited Oct 6, 2014 at 8:51 PM
lwischik wrote:
The scenario the switched our minds was the typing process, e.g. I type var x = "hello | and at this point I realize I want an interpolated string. In one design I have to cursor back to the start of the string and insert a $. In the other design I can continue typing right where I am.
In my opinion, even if this notation favours the writers, it makes the code harder to read. Prefix makes it obvious from the very beginning that the string is actually not a string constant, but a complicated expression. With the \{ notation, the reader might well be halfway through the string content when realizing that the string is actually not a constant string. One can even not notice the difference if the string is long and not fully visible in the edit area (which opens nasty possibilities for the hidden "surprise" code in holes).

Another confusion could be about why the code
const string s = "Innocently-looking string here, and here, and 1000 more characters here, and at last \{ this }";
fails to compile. With prefix that would be obvious from the first glance.

I am in the prefix camp.
Oct 6, 2014 at 9:22 PM
Edited Oct 6, 2014 at 9:26 PM
Also if use the existing style of String.Format for String.Interpolation you can share a vast majority of the required code, the only difference is the code to recognise identifiers rather arg index. That is also is made easier by that fact most of the exists already in roslyn.
      Head ::= '$' '"'
      Tail ::= '"'
   IString ::= Head Body Tail
      Body ::= (TextBlock* ArgHole+ TextBlock*)+ 
   ArgHole ::= '{' Identifer (',' Alignment )? (';' Formatting )? '}'
 TextBlock ::= "{{" | "}}" | 
 Alignment ::= ('+'|'-')? NumericCharacter+
Formatting ::= etc
The grammar for Identifiers is a little more complex
          Identifier ::= NonEscapedIdentifier [ TypeCharacter ] | EscapedIdentifier
NonEscapedIdentifier ::= < IdentifierName but not Keyword >
   EscapedIdentifier ::= [ IdentifierName ] 
      IdentifierName ::= IdentifierStart [ IdentifierCharacter+ ]
     IdentifierStart ::= AlphaCharacter | UnderscoreCharacter IdentifierCharacter
 IdentifierCharacter ::= UnderscoreCharacter |
                              AlphaCharacter |
                            NumericCharacter |
                          CombiningCharacter |
                         FormattingCharacter
      AlphaCharacter ::=   < Unicode alphabetic character (classes Lu, Ll, Lt, Lm, Lo, Nl) >
    NumericCharacter ::= < Unicode decimal digit character (class Nd) >
  CombiningCharacter ::= < Unicode combining character (classes Mn, Mc) >
 FormattingCharacter ::= < Unicode formatting character (class Cf) >
 UnderscoreCharacter ::= < Unicode connection character (class Pc) >
 IdentifierOrKeyword ::= Identifier | Keyword
As an additional benefit you could also get more compile-time warnings / errors. via String.Format Diagnostics
Oct 6, 2014 at 9:47 PM
lwischik wrote:
Halo_Four wrote:
These meeting notes are obsolete. The conclusion during the May meeting was to use a prefix on the string literal to indicate that it is an interpolated string, e.g. $"hello {customer.name}"
We had a follow-up meeting in September to revisit string interpolation, and played around with how everything feels, and actually came to prefer "hello \{customer.name}" for C#, but still $"hello {customer.name}" for VB. That's what we've implemented so far in prototype form. It won't be out in CTP4, but it will likely be out in the release after that for C# (and a little later for VB).

When it does come out, we'll want people to play around with it in practice to see how it feels. And for people to play with it in practice before voicing further strong opinions :)

The scenario the switched our minds was the typing process, e.g. I type var x = "hello | and at this point I realize I want an interpolated string. In one design I have to cursor back to the start of the string and insert a $. In the other design I can continue typing right where I am.

-- Lucian from VB/C# LDM
That sounds a lot like a reason after the fact. A problem created for the solution.

So, I'm on C# 5.0 (or VB - which version is it now?) happily coding away and I start writing var key = "super_dooper_ - oops! Looks like I need to add a few dynamic values to this. And several, because, otherwise, I'd just use string concatenation.

Bummer, I need to go all the way back to add string.Format(.

C# 6.0 comes out and, same situation. But now I'm lucky enough to be a C# developer and I can code away without having to go back and add something to the beginning of the string. I feel sorry for those poor VB developers that still have to do that.

Now comes the time read code and Dim key = $"super_dooper_{customer.LastName}_{customer.FirstName}_other_stuff" is immediately perceived as an interpolated string without needing to read the string contents. On the other hand , var key= "super_dooper_\{customer.LastName}_\{customer.FirstName}_other_stuff" requires me to scan the string contents until I find the first \{ or until the end just to know if it's interpolated or not.

There's only one reason I can think of for \{ being somewhat better than a prefix, but I'm not going to say what it is.

I'm really just trying to understand why is this such a good idea.
Oct 6, 2014 at 10:17 PM
Halo_Four wrote:
That is standard and expected behavior for any c-heritage language going back over 40 years. The \ is the important part of the escape sequence. Notably, \{ is currently illegal syntax so to co-opt it for interpolation cannot break any existing code. The difference here with VB isn't that important because BASIC's heritage does not have escape sequences in strings, save for the doubling of double-quotes in order to insert a single double quote.
The normal idiom for C is that a backslash in a string literal turns itself and the next character or, if the next character is an "x", "X", or a digit, the next few characters, into a single character that would otherwise be hard to type. I can't think of any cases where any other C-like language prior to Swift used a backslash within a string to effectively switch to a different processing mode, nor do I see any advantage to such a structure versus moving the non-literal content outside the quotes.

Saying Header = $"Coordinates: ["(X)","(Y)"]"; would be nearly as concise as the format using \{ but avoid any confusion about what is part of the string literal and what isn't. Actually, what I'd like would be a means by which a list of literal strings and parenthesis-delimited values would be transformed so as to be equivalent to "Coordinates: [{0},{1}]",(X),(Y), at least in contexts where the a suitable overload had a parameter of type String which was tagged with an attribute indicating it should accept that style of string.
Oct 6, 2014 at 10:23 PM
I agree with Paulo, why different syntaxs for the same feature? Why the double effort?
Coordinator
Oct 6, 2014 at 11:02 PM
AdamSpeight2008 wrote:
I agree with Paulo, why different syntaxs for the same feature? Why the double effort?
The implementations necessarily have to be pretty much completely different between C# and VB.

Both languages have an "incremental parser" which only reparses the bits of the parse tree affected by where you're typing right now. In VB this can easily be line-based like it's always been. C# doesn't have lines so it has more complicated rules.

Also VB already has a stateful lexer+parser, and needed it to deal with XML literals. We haven't got there in the prototype yet, but it might be reused for string interpolation. C# has never had a stateful lexer+parser.
Oct 6, 2014 at 11:14 PM
Edited Oct 6, 2014 at 11:45 PM
I probably come off as being much more of an advocate either way than I am. I'm pretty ambivalent as to the syntax for this feature. Honestly I'm more defending the escape-sequence syntax more out of sheer Devil's Advocacy towards the arguments against it. :)

I can see advantages and disadvantages to all of the major proposed syntaxes. Escape sequences fit with the existing language structure. To "interpolate" an existing string requires little additional effort as the contents of the existing string could never run afoul of the new syntax. That said, at first glance it may be difficult to discern whether or not a string literal is really a literal or a calculated value, which can be problematic.

The prefix syntax does make it easy to determine if a string is interpolated at a glance. But it also adds the problem of having to worry about two separate escaping concerns. You couldn't always just slap a prefix onto an existing string and expect the code to compile as if that existing string contained curly brackets that could cause compiler errors, or worse, unintended interpolation.

Backticks are like prefixes in that they are immediately clear to be different from string literals, and they could help solve what I think might be one of the more common use cases of string interpolation which is inline non-localized pluralization by allowing expressions to contain non-interpolated string literals using double-quotes.

That list is certainly not exhaustive, just what I thought up while doing the dishes. :)

That said, it seems that the Roslyn team thinks that escape sequences are the way to go for the time being, enough so that CTP4 will have that syntax for string interpolation. I'd love to see their notes from last month as it may shed some additional light onto the thinking of the language team. I'm sure that release will be immediately followed with a surge in opinions on this forum again.
Oct 6, 2014 at 11:16 PM
lwischik wrote:
AdamSpeight2008 wrote:
I agree with Paulo, why different syntaxs for the same feature? Why the double effort?
The implementations necessarily have to be pretty much completely different between C# and VB.

Both languages have an "incremental parser" which only reparses the bits of the parse tree affected by where you're typing right now. In VB this can easily be line-based like it's always been. C# doesn't have lines so it has more complicated rules.

Also VB already has a stateful lexer+parser, and needed it to deal with XML literals. We haven't got there in the prototype yet, but it might be reused for string interpolation. C# has never had a stateful lexer+parser.
Technical reasons aside, I'd argue that they're different languages so they should have different syntax to fit in with the rest of the language. I'm curious, though, if VBvNext is going to allow newlines in string literals where does interpolation fit in with that?
Oct 6, 2014 at 11:35 PM
Argument for prefix syntax;- I reckon that on balance you'll spend more time reading and understanding code, rather than writing it.
Coordinator
Oct 6, 2014 at 11:37 PM
Halo_Four wrote:
I'm curious, though, if VBvNext is going to allow newlines in string literals where does interpolation fit in with that?
Depends how it turns out when we come to implement it in VB. My guess is that multiline stuff will be hard to mix with string interpolation, because of the "incremental parsing" issues, and so we'd restrict $"..." to be single-line in VB at least for now. It's something that can be relaxed in future. C# interpolated strings will also be single line and I'm not sure how it could be made multi-line.
Oct 6, 2014 at 11:44 PM
lwischik wrote:
Halo_Four wrote:
I'm curious, though, if VBvNext is going to allow newlines in string literals where does interpolation fit in with that?
C# interpolated strings will also be single line and I'm not sure how it could be made multi-line.
With a @ prefix! :)
Oct 6, 2014 at 11:49 PM
AdamSpeight2008 wrote:
Argument for prefix syntax;- I reckon that on balance you'll spend more time reading and understanding code, rather than writing it.
Quite. The first statement I wrote under "prefixes" was intended to convey that it was easier to discern at a glance and I have rewritten it to be clearer.

At this point I guess we'll see where things falls after CTP4. I can't imagine that there is a lot of room left for reworking features.
Oct 6, 2014 at 11:58 PM
PauloMorgado wrote:
lwischik wrote:
Halo_Four wrote:
I'm curious, though, if VBvNext is going to allow newlines in string literals where does interpolation fit in with that?
C# interpolated strings will also be single line and I'm not sure how it could be made multi-line.
With a @ prefix! :)
I do think think that if the two were to be combined that only prefixing would make sense as combining the symbols @ and $ at least declare a pretty specific intent. Since @ shuts down escape sequence parse it wouldn't make any sense at all to then rely on it for interpolated verbatim strings.

However, I actually kind of like having the limitation of not being able to combine verbatim strings and interpolated strings if only to discourage programmers from writing really long interpolated strings. At the end of the day, syntax aside, this feature encourages a number of bad practices. I'd honestly rather see easier support in VS for writing interpolated resources, e.g. including named placeholders in the resource strings which the project item converts into functions accepting those values as parameters.
Developer
Oct 7, 2014 at 5:42 AM
PauloMorgado wrote:
Now comes the time read code and Dim key = $"super_dooper_{customer.LastName}_{customer.FirstName}_other_stuff" is immediately perceived as an interpolated string without needing to read the string contents. On the other hand , var key= "super_dooper_\{customer.LastName}_\{customer.FirstName}_other_stuff" requires me to scan the string contents until I find the first \{ or until the end just to know if it's interpolated or not.
Why do you care if it is interpolated or not? It is a string. You have exactly the same issue with string concatenation - you have to read the whole expression to see if there in an end-of-string and a concatenation somewhere in there.

This is a common reaction to new language features. When people first encounter a new language feature they despair that it does not have more syntactic ceremony so they can easily see where the new feature is used in the code. Then after they've gotten used to it they bemoan all the ceremony around the new feature, and wonder why it couldn't have been simpler.
There's only one reason I can think of for \{ being somewhat better than a prefix, but I'm not going to say what it is.
Actually, a prefix would have made the feature easier to parse. We went with this implementation because it is the way we believe the feature should be because it is easier to use when typing.
Oct 7, 2014 at 5:55 AM
nmgafter wrote:
Why do you care if it is interpolated or not? It is a string. You have exactly the same issue with string concatenation - you have to read the whole expression to see if there in an end-of-string and a concatenation somewhere in there.
You've got a point there.
There's only one reason I can think of for \{ being somewhat better than a prefix, but I'm not going to say what it is.
Actually, a prefix would have made the feature easier to parse. We went with this implementation because it is the way we believe the feature should be because it is easier to use when typing.
I have no doubt about that.
This is a common reaction to new language features. When people first encounter a new language feature they despair that it does not have more syntactic ceremony so they can easily see where the new feature is used in the code. Then after they've gotten used to it they bemoan all the ceremony around the new feature, and wonder why it couldn't have been simpler.
I agree with you in general but not in this particular case: me!

Because I'm not against it, just because, I've been trying to sell it to myself and I'm almost there. I'm sure the reasons are scattered around these posts and I just didn't read them with enough attention, but I'm already 99.9% there.
Oct 7, 2014 at 8:50 AM
@lwischik, nmgafter
I'll support other who were asking for custom interpolator - how do you see this playing with \{ syntax? If the main argument is "it is easier to write, you don't need to jump to the beginning of the string", then it should come as postfix syntax. This would be weird. Only ++ and -- use postfix in c#. And these are applied to identifiers, which are not that long and thus don't require scanning line far to the right. This is not the case with strings, which will be much longer. If you want to use prefix syntax (I really like the way JS implements this), then the whole argument that you don't need to go to the beginning is out of the table. Anyway - the golden rule to remember is "write once - read many times". IMHO \{ syntax goes against this.

nmgafter wrote:
Why do you care if it is interpolated or not? It is a string. You have exactly the same issue with string concatenation - you have to read the whole expression to see if there in an end-of-string and a concatenation somewhere in there.
Assuming that expression is long and contains few string concatenations, etc - sane developers will split this into few lines of code, so that it is easier to read. I can do this with string concatenation, so that each concatenated part takes own line if necessary. I cannot do this with interpolated string - it will be one liner. To be fair, string interpolation by its nature will remove much of the "ceremony", making the whole expression much easier to read. Will need to play with it to see how this is balancing.
Why do I care if this is interpolated string or not? Really good question. I need to play with the feature to see if this will be actual issue or not, but I can see potential pain point - debugging. Not sure if I am correct, but I think, the autos, locals and watch windows will show expanded content of the string. It might not be that obvious where the error is, if it is inside one of the interpolated expressions. It might be helpful to see that particular string was interpolated to find the root cause of a bug faster. However, this can be addressed at the IDE level, in debugger.
Oct 7, 2014 at 11:49 AM
nmgafter wrote:
Why do you care if it is interpolated or not? It is a string. You have exactly the same issue with string concatenation - you have to read the whole expression to see if there in an end-of-string and a concatenation somewhere in there.
Well, this is probably a very uncommon scenario but I have worked with code where it was important to know whether or not the string was an actual literal as the callee required an interned string reference and would fail otherwise. Not to get into too much detail but it was a binary-based logging mechanism where the format strings were written exactly once.
Oct 7, 2014 at 1:28 PM
Please use the prefix approach in C#:
  • Lucian, on other features you favor reading over writing, that also applies here
  • Neal, of course we care whether it is interpolated. Unless you've done magic, interpolation will be enough slower to matter a lot in a tight loop. And the concatenation will always be an option to consider if you discover perf issues.
  • I'm dyslexic on slashes. The less you use backslash the better
  • I'm cross language. The less you introduce differences, the happier I am
  • $" is elegant, and because C# coders know @" they will always immediately know "special string, I wonder what kind of special string".
  • "{" looks like you're escaping to INCLUDE the brace, not drop to special processing
  • The debugging Find RegEx for "This is a (snail|{)" is way better than "This is a (snail|\{)" (did I get that right?)
  • The RegEx above indicates one of the ways backslash is used in RegEx, to include a special character in the search - this is the exact opposite of what is intended by the interpolation proposal - with the same character. Yes, I know RegEx also escapes in a similar manner (escaping into special ops) such as \S, but that is (annoying and) escaping non-punctuation. Think RegEx, especially in search isn't important? After fixing breakpoints (LOVE YOU GUYS) making non-wimpy find is the next most important thing to do, and RegEx is already there as the basis (create a drop dead RegEx Find peek editor, and your done).
I was initially against this feature because it hides a string method usage and therefore can hide a perf problem. I would have preferred an interpolation that rested inside String.Format for clarity in what's really happening.

So, let us have the prefix so we can at least find these things! I can search code for $" if I find a boatload of time in String.Format scattered throughout my code.

We'll survive either way. But please don't tell us we can work this out after it appears in a CTP if that isn't realistic. If we're close enough to lock down that there won't be time to change, please reconsider now. And if we can get a CTP 4.1 that just has this feature, and maybe getter-only auto-props, it is absolutely worth it.

Kathleen
Oct 7, 2014 at 1:58 PM
One more thing..

Can some show the string creation that will create a string that when passed to a RegEx will result in this output, assuming the backslash approach?

"flskj sagsd {sdfdf, sfdsdf}"

OK, so maybe the answer is, you have to use the at sign in front to get a meaningful RegEx string, and that's OK since you have to use the @ sign today.

var name = "Lu";
var regexString1 = "asdf {name}";
var regexString2 = @"asdf {name}";

Both are passed through regex. The first results in "asdf Lu" and the second results in an attempt to match "asdf" plus any upper case, right?

var name = "Fred";
var regexString1 = "asdf ${name}";
var regexString2 = @"asdf ${name}";

Passed to RegEx, the first is "asdf Fred" and the second is "asdf" plus a substitution to a group named "name"

I actually don't think this is as strong as the readability and performance arguments, because @ has always fundamentally altered output.

But most of the RegEx escapes aren't legal prior to this proposal, so you're led into the pit of success for using the backslash in the right place. This will no longer be true with the backslash proposal for string interpolation.

Kathleen
Oct 7, 2014 at 3:36 PM
I agree with everything Kathleen said
Oct 7, 2014 at 4:52 PM
Edited Oct 7, 2014 at 4:54 PM
Another vote against backslash, for all the reasons everyone else has already stated. As a big fan of the already-existing @-strings, I think there's a good precedent there for prefixes, and I agree that it seems more readable (which I view as being more important than easier to type). Also, backslash would add confusion when combined with @ - would the backslash be treated as a literal or not in that case?

I'd be mostly interested in using this feature for building pathnames and regex expressions. So, I'd want it to work in combination with @-strings, and to have an uncluttered way to specify InvariantCulture. So, thanks to KathleenDollard for covering some of the scenarios that seem to be getting overlooked; I'd love to see more discussion of using this in scenarios where you aren't building a string to be displayed to a user.
Oct 7, 2014 at 5:02 PM
For the prefix vs question I'm voting for a prefix. You'll break code if simply try to figure out what I want based upon some arbitrary characters in a string. I happen to already use {xyz} in a custom formatting library we use. Assuming that would never appear preceded by a slash (or anything else) is just wrong. A prefix makes it clear (like the verbatim string).
Coordinator
Oct 7, 2014 at 5:10 PM
btjdev wrote:
I'd be mostly interested in using this feature for building pathnames and regex expressions. So, I'd want it to work in combination with @-strings, and to have an uncluttered way to specify InvariantCulture.
The only things that display differently with InvariantCulture are DateTime and floats.

DateTime's default InvariantCulture display is generally wrong for API usage like pathnames and regex and uris and serialization -- instead you'd use a format string like "hello \{ DateTime.Now :o}" or similar.

As for floats, InvariantCulture alone is a bit dodgy since it sometimes flips into scientific notation. I think you'd either do a combination of InvariantCulture and format string INV$"hello { val : f4}", or you'd do it through the format string alone "hello \{ val : "0\\.00"} ".
Oct 7, 2014 at 5:13 PM
CoolDadTx wrote:
For the prefix vs question I'm voting for a prefix. You'll break code if simply try to figure out what I want based upon some arbitrary characters in a string. I happen to already use {xyz} in a custom formatting library we use. Assuming that would never appear preceded by a slash (or anything else) is just wrong. A prefix makes it clear (like the verbatim string).
The backslash syntax doesn't break any existing code. Including \{ in a non-verbatim string is currently not legal C#, you are required to escape the backslash, e.g. "\\{foo}". Interpolation won't be permitted with verbatim strings with or without backslashes so it's also not a concern there.
Oct 7, 2014 at 8:31 PM
Halo_Four wrote:
The prefix syntax does make it easy to determine if a string is interpolated at a glance. But it also adds the problem of having to worry about two separate escaping concerns. You couldn't always just slap a prefix onto an existing string and expect the code to compile as if that existing string contained curly brackets that could cause compiler errors, or worse, unintended interpolation.
Why is it necessary to use escapes for holes? Why not simply put the hole content outside the quoted part of the string? It's not presently legal to precede or follow a string literal with a value enclosed in parentheses, so $"Coordinate is ["(x)","(y)"]"; should be fine. If someone wants to space it more widely as $"Coordinate is [" (x) "," (y) "]"; that would be fine too. I don't like the idea of having non-literal stuff inside quotes without a good reason, and I don't think I've seen a good reason.
Oct 7, 2014 at 8:56 PM
supercat wrote:
Halo_Four wrote:
The prefix syntax does make it easy to determine if a string is interpolated at a glance. But it also adds the problem of having to worry about two separate escaping concerns. You couldn't always just slap a prefix onto an existing string and expect the code to compile as if that existing string contained curly brackets that could cause compiler errors, or worse, unintended interpolation.
Why is it necessary to use escapes for holes? Why not simply put the hole content outside the quoted part of the string? It's not presently legal to precede or follow a string literal with a value enclosed in parentheses, so $"Coordinate is ["(x)","(y)"]"; should be fine. If someone wants to space it more widely as $"Coordinate is [" (x) "," (y) "]"; that would be fine too. I don't like the idea of having non-literal stuff inside quotes without a good reason, and I don't think I've seen a good reason.
You've mentioned your syntax before and it gained no traction either in the forum or with the C# team so as far as I can tell it is not in contention. I'm more interested in discussing what the C# team is likely to actually implement.

The majority of languages which offer string interpolation do include the placeholder directly within the string literal. It is what is expected of the feature, for better or worse.
Oct 7, 2014 at 10:08 PM
@supercat
An issue with that format $"Coordinate is [" (x) "," (y) "]"; is the use of parenthesis. Parenthesis are used to force the order of evaluation of an expression, put some forget that there is an implicit parenthesis around the expression. ( (x) + (y) ), thus valid to include around the expression.
Also how would you distinguish between your form of string interpolation and an malformed line, that contained a string eg "Coordinate is [" + (x) + "," + (y) "]" ;
Oct 7, 2014 at 11:07 PM
AdamSpeight2008 wrote:
@supercat
An issue with that format $"Coordinate is [" (x) "," (y) "]"; is the use of parenthesis. Parenthesis are used to force the order of evaluation of an expression, put some forget that there is an implicit parenthesis around the expression. ( (x) + (y) ), thus valid to include around the expression.
I'm not quite sure what you mean. If the syntax requires the existence of at least one set of braces, what difference would more make?
Also how would you distinguish between your form of string interpolation and an malformed line, that contained a string eg "Coordinate is [" + (x) + "," + (y) "]" ;
I don't think putting an expression enclosed in parentheses next to a string literal is ever valid under existing rules, is it? There could be ambiguity if a + appears before the last string literal, but I would think the result of the expression would either be the same as if the last literal were part of the interpolated string or else would refuse compilation.
Oct 8, 2014 at 12:29 AM
Kathleen,

I've been very firmly, as usually, on the same stand you are. However, I'm now almost 100% in favor of the proposed solution for C#.

In C# we have two types of string literals (§2.4.4.5): regular string literals and verbatim string literals.

Verbatim string literals are interpreted verbatim with the only exception of quoted-escape-sequences.

Even if the implementation of interpolated strings used a prefix it would not make sense for a string being both interpolated and verbatim because, if it is verbatim, it can't be interpolated. At this point I'm, let's say, 75% in favor of the prefix.

Now, having verbatim string literals out of the way, let's see what you can write in a regular string literal and what does the compiler do with it. To put it simple, regular string literals are strings (how unexpected!) of characters enclosed by "s. But the compiler doesn't just take those characters and throw them on the IL string. If you write \", the compiler will convert it to ". You can also write control characters and characters specified by their hexadecimal code and/or their Unicode code (§2.4.4.4). The common denominator of all these interpretations by the compiler is that they are prefixed by \ character.

If you look at the how the space character is expressed in hexadecimal form (\x0020) you might think of it as an hexadecimal number prefixed by \x. Now try to think of it as an escaped expression (x0020) and that all escaped sequences are prefixed by \, being a control character, an Unicode specification, a ' or a ". And holes ({expr}) are also escaped sequences (\{expr}).

Does that make sense? I think so!

Does that look like good old C#? I think so!

I'm now 100% in favor of \{expr} for declaring holes in interpolated strings.

As for regular expressions, you already have that problem if you are looking for, say, new lines. The regular expression is \n but you have to write it either as a verbatim string literal (@"\n") or as regular string literal where you have to escape the escape character ("\\n").
Oct 8, 2014 at 1:47 AM
Paulo,

… interpolated and verbatim…

I am not suggesting that we allow the same string to be a verbatim string and a interpolated string. One or the other, agreed.

No matter what we do, we now have three kinds of strings. We cannot change this and the more we hide the fact, the more trouble we will be in
  1. verbatim strings
  2. “normal” strings which support escaped syntax, which escape the thing immediately following – the thing following may be a multi-digit number, or a Unicode character string, but it is still the thing that immediately follows. Yes, arguable “x” is (in context) punctuation, but it is a widespread prefix for a hex number, not a new thing.
  3. interpolated strings
1 and 2 are only slightly different. I am not aware of any fundamental rewriting that occurs to result in different behavior in relation to the .NET framework.

3 is entirely different. It is not a simple string assignment. It is a call to a .NET framework format method posing as a string assignment.

I spent time down the path of twisting my head around this being escaping on steroids. It’s not. Escaping has a history. Holes have a history. They are different things. Escaping exists in many languages and even a small amount of action in odd Unicode (uppercase). This is immediately data with flexible formatting, and I’m quite sure there will be pressure for expressions in a future version (as is appropriate).

Just for confirmation, can someone show me this VB example in C# with the backslash holes approach? How many backslashes do I need?

Dim filepath = $"{drive}:{path}{file}.{ext}"

And where did this feature land for culture. A lot was said, and I don’t know the outcome.

Kathleen
Oct 8, 2014 at 2:02 AM
KathleenDollard wrote:
Just for confirmation, can someone show me this VB example in C# with the backslash holes approach? How many backslashes do I need?

Dim filepath = $"{drive}:{path}{file}.{ext}"
string filepath = "\{drive}:\\\{path}\\\{file}.\{ext}";
You'd need most of them anyway just to escape the escape sequences.
And where did this feature land for culture. A lot was said, and I don’t know the outcome.
Defaults to current culture, the same as String.Format does when not passed a specific culture. This is consistent with every implementation of string interpolation that I've seen in other languages as well.
Oct 8, 2014 at 2:45 AM
I second the questions above about custom interpolation. I know it was never a promised feature, but at least with the $" syntax it seemed more plausible.
Oct 8, 2014 at 9:19 AM
KathleenDollard wrote:
Paulo,

… interpolated and verbatim…

I am not suggesting that we allow the same string to be a verbatim string and a interpolated string. One or the other, agreed.

No matter what we do, we now have three kinds of strings. We cannot change this and the more we hide the fact, the more trouble we will be in
  1. verbatim strings
  2. “normal” strings which support escaped syntax, which escape the thing immediately following – the thing following may be a multi-digit number, or a Unicode character string, but it is still the thing that immediately follows. Yes, arguable “x” is (in context) punctuation, but it is a widespread prefix for a hex number, not a new thing.
  3. interpolated strings
1 and 2 are only slightly different. I am not aware of any fundamental rewriting that occurs to result in different behavior in relation to the .NET framework.

3 is entirely different. It is not a simple string assignment. It is a call to a .NET framework format method posing as a string assignment.

I spent time down the path of twisting my head around this being escaping on steroids. It’s not. Escaping has a history. Holes have a history. They are different things. Escaping exists in many languages and even a small amount of action in odd Unicode (uppercase). This is immediately data with flexible formatting, and I’m quite sure there will be pressure for expressions in a future version (as is appropriate).

Just for confirmation, can someone show me this VB example in C# with the backslash holes approach? How many backslashes do I need?

Dim filepath = $"{drive}:{path}{file}.{ext}"

And where did this feature land for culture. A lot was said, and I don’t know the outcome.

Kathleen
It might be a call to a .NET framework format method.
Oct 8, 2014 at 1:20 PM
HaloFour- thanks for the clarifications on the file path and culture. I think that is the correct culture to use. It carries the same issues and benefits as the current usage and that's appropriate.

I think I was unclear. There is no backwards compatibility break. With this proposal, people will need to think of strings differently, rather than thinking of a new kind of string.

Whether it happens all the time or some of the time (compiler optimization), these new strings may use a much difference approach in creation, and may is enough here. I've been meaning to do some profiling for both perf and memory usage of String.Format vs. simple concat, and because I'm not very good at that, it will take a lot of time I don't really have. So I looked it up and found numbers from 2009 and 2010 that differ significantly.

http://geekswithblogs.net/BlackRabbitCoder/archive/2010/05/10/c-string-compares-and-concatenations.aspx
http://weblogs.asp.net/jevgeni/appending-string-in-c-string-vs-string-format-vs-stringbuilder

BlackRabbit's numbers were only 20% slower and have a negligible difference in memory. That surprised me, because I anticipated numbers more like Jevgeni's numbers. There may be a CLR version difference as well, but the main difference is that BlackRabbit is working to isolate the impact of single line concatenation, where Jevgeni is building up a large string. Jevgeni's numbers are not very important here, as building up a string is obviously the job for String Builder.

So, can someone from the team comment on the range of performance difference we might see in statements that are not likely to be optimized, but that wouldn't be using Format. I'm not quite sure what that would be, but I don't need to see perf numbers, just a general sense.

If the difference is 20% or less, I'm going to downplay the performance argument. 20% will be a lot in a very small number of cases (assuming StringBuilder cases aren't relevant). It won't be measurable in most applications, and if that's the case, overlooking usage on code review of performance focused code isn't a big deal (optimization is sometimes appropriate during development, that is simply good habits).

I still strongly favor the prefix for all the reasons I stated Tuesday. But, if the perf hit for single line usage of format vs. concat is in the 20% range, the argument of a hidden perf problem is pretty weak and limited.

Kathleen
Coordinator
Oct 8, 2014 at 2:08 PM
KathleenDollard wrote:
I've been meaning to do some profiling for both perf and memory usage of String.Format vs. simple concat
In planning this feature we anticipated that the compiler would be able to optimize where necessary...
  • If there are no format specifiers, the compiler could just call .ToString() itself on the holes and then String.Concat. This will be more efficient in all cases than String.Format since it avoids the runtime parse step that String.Format has to do, and it can also avoid boxing, e.g. String.Format("{0}", an_integer).
  • If there are no holes then the compiler can use a string literal.
It's a question of how much we want to hard-code knowledge about the functioning of String.Format into the optimizing step.


-- Lucian Wischik, VB/C# language team
Oct 8, 2014 at 4:26 PM
lwischik wrote:
btjdev wrote:
I'd be mostly interested in using this feature for building pathnames and regex expressions. So, I'd want it to work in combination with @-strings, and to have an uncluttered way to specify InvariantCulture.
The only things that display differently with InvariantCulture are DateTime and floats.

DateTime's default InvariantCulture display is generally wrong for API usage like pathnames and regex and uris and serialization -- instead you'd use a format string like "hello \{ DateTime.Now :o}" or similar.

As for floats, InvariantCulture alone is a bit dodgy since it sometimes flips into scientific notation. I think you'd either do a combination of InvariantCulture and format string INV$"hello { val : f4}", or you'd do it through the format string alone "hello \{ val : "0\\.00"} ".
Thank you, nice analysis and summary of the language culture scenarios.

Whoever is implementing the compiler translation for this should verify that Code Analysis rule CA1305: Specify IFormatProvider will be OK with whatever IL is generated under the hood.