String interpolation, after a healthy debate on discord

WebFreak001 d.forum at webfreak.org
Thu Dec 9 21:06:11 UTC 2021


We had an argument on the discord about the 
[YAIDIP](https://github.com/John-Colvin/YAIDIP) proposal and a 
simpler syntax form `identifier""` based on Ola Fosheim Grøstad's 
proposal, and similar to JavaScript's template string syntax.

We came to the conclusion that the simpler syntax would just be 
that - a simpler syntax - and still require the YAIDIP, but with 
that give nice improvement opportunities.

The simpler syntax would be an additional special function 
calling syntax (only valid for functions accepting the 
interpolated strings with the header as suggested by YAIDIP)

Example:
```d
string text(T...)(T args) if (isInterpolatedString!T) { ... }

// could then be called:

text"Hello $name!";

// translates to:
text(i"Hello $name!");

// translates to:
text(InterpolationHeader!("Hello ", "name", "!"), "Hello ", name, 
"!");
```

For any new user the `text(i"Hello $name!")` or `i"Hello 
$name!".text` syntax could be daunting for regular use, possibly 
even hindering the adaption of interpolated strings for regular 
string usage.

Like the `str.to!int` syntax has proven an advantage over 
`str.to!(int)` and how `foo.filter!isNumber.map!...` has proven 
advantages over `foo.filter!isNumber().map!...()`, these 
interpolated string functions would be useful in everyday 
application by GC users, with phobos functions like `text` or 
`format`, or by any other users with custom functions, and drive 
adoption of the YAIDIP interpolated string suggestion in user 
code.

advantages of the simple syntax:
- very easy syntax on the common GC-use of just creating a string 
from an interpolated string by a user: `setContent(text"Hello 
$name!", text"Welcome on your profile, $name.")`
- familiar to JS developers having used their [template 
strings](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals)
- the return types of the handling functions can be regular 
structs (non-templated) and be used with functions accepting them 
to ensure type-safety and also allow exporting the functions in 
DLLs / static compiled libraries

disadvantages:
- not usable in template arguments with type arguments (like 
`foo!i"$bar $int"`) - need to use the `i` strings there
- could clash with existing `i""`/`r""`/`q""` string types - 
these would need to be excluded as special cases

Example how it could look using postgresql (dpq2) dub package:

before:

```d
double d = -1234.56789012345;
string text = "first line\nsecond line";
Nullable!string nullString;
int[][] arrs = [[1, 2, 3], [4, 5, 6]];

QueryParams p;
p.sqlCommand = "SELECT "~
     "$1::double precision as double_field, "~
     "$2::text, "~
     "$3::text as null_field, "~
     "array['first', 'second', NULL]::text[] as array_field, "~
     "$4::integer[] as multi_array, "~
     `'{"float_value": 123.456,"text_str": "text string"}'::json 
as json_value`;

p.argsVariadic(
     d,
     text,
     nullString,
     arrs
);

auto r = conn.execParams(p);
```

after YAIDIP:

```d
double d = -1234.56789012345;
string text = "first line\nsecond line";
Nullable!string nullString;
int[][] arrs = [[1, 2, 3], [4, 5, 6]];

QueryParams sql(T...)(T interpolatedString) { /* process the 
fields here, create string with incrementing variables, calling 
argsVariadic on the QueryParams object and returning it */ }

QueryParams p = sql(i`SELECT
         $d::double precision as double_field,
         $text::text,
         $nullString::text as null_field,
         array['first', 'second', NULL]::text[] as array_field,
         $arrs::integer[] as multi_array,
         '{"float_value": 123.456,"text_str": "text 
string"}'::json as json_value`);

auto r = conn.execParams(p);
```

using the simple syntax would allow you then writing `` sql`...` 
`` instead of ``sql(i`...`)``

Some code-bases (internal usage in packages/programs or in 
company code-bases) could opt for a really tiny name like `alias 
T = std.conv.text; string s = T"Hello $name!"`.

The formal definition for this short syntax could either be

```
InterpolatedStringCall:
     Identifier " DoubleQuotedCharacters_opt " StringPostfix_opt
     Identifier ` WysiwygCharacters_opt ` StringPostfix_opt
```

allowing only simple identifiers

or

```
InterpolatedStringCall:
     Expression " DoubleQuotedCharacters_opt " StringPostfix_opt
     Expression ` WysiwygCharacters_opt ` StringPostfix_opt
```

allowing any arbitrary expressions, immediately followed by a 
string like `(foo("hello")) " world"` which is then interpreted 
like an interpolated string.

What do you think? Would this be essential for interpolated 
string adoption in user code or be useless?


More information about the Digitalmars-d mailing list