April 20, 2017
On Thursday, 20 April 2017 at 18:28:30 UTC, Atila Neves wrote:
> writeln($"{a} times 3 is {a * 3}");
>
> is even marginally better than
>
> writeln(a, " times 3 is ", a * 3);  // look ma, works right now!

Matching up the correct commas and quotes is a bit of a pain in the latter.

Though I don't think it is a big deal... I think most string building is a mistake anyway, as in you should find some entirely different way to it rather than embedding variables like that. But if I am going to do it it, the writeln(a, ",", "\") is like my least favorite way (yet I use it a lot because it is in there)
April 20, 2017
On 04/20/2017 06:23 AM, Martin Tschierschke wrote:
> and this not?
>
> import scriptlike;
>
> // with exho definition outside the scope of the used vars
>
> auto exho(string x)(){return mixin("writeln("~interp!x~")");}
>
> void main()
> {
>
>    auto name = userInput!string("Please enter your name");
>    auto age = userInput!int("And your age");
>
>    writeln(mixin(interp!"${name} you are app. ${age*365} days old"));
>
>    exho!"${name} and this are app ${age*365*24} hours!";
> }

The definition for:

exho!"${name} and this are app ${age*365*24} hours!"

...once the template is instantiated and the mixin is evaluated, is turned into this:

auto exho()
{
	return writeln("${name} and this are app ${age*365*24} hours!");
}

But, there is no identifier "name" or "age" within the scope of exho.

However, if exho is instead defined as a nested function WITHIN main, then as with any (non-static) nested function, it DOES have access to the local variables from the outer function.

This, incidentally, is exactly why interp!"..." needs to be a string mixin in the first place.

April 20, 2017
On 04/20/2017 02:40 PM, Nick Sabalausky (Abscissa) wrote:
>
> auto exho()
> {
>      return writeln("${name} and this are app ${age*365*24} hours!");
> }
>

Correction:

auto exho()
{
    return writeln(
        ""~
        _interp_text(name)~
        " and this are app "~
        _interp_text(age*365*24)~
        " hours!"
    );
}

Where _interp_text is really just a thin wrapper over std.conv.text.

Again, note that `name` and `age` are undefined symbols unless exho is defined as a nested function within the function that actually defines `name` and `age`.

And again, this is exactly why interp!"..." was forced to work by returning a string for the caller to mixin.

April 20, 2017
Also how various kinds of strings would work?
r$"{can}\i\has{slashes}"
$`same {here}`
$q{{balanced}braces}

For templates it's straightforward.
April 20, 2017
On 04/20/2017 02:28 PM, Atila Neves wrote:
>
> I don't understand how
>
> writeln($"{a} times 3 is {a * 3}");
>
> is even marginally better than
>
> writeln(a, " times 3 is ", a * 3);  // look ma, works right now!
>
> It's not even fewer characters.
>
> Atila
>

The latter IS pretty good, I use it often. It's *VASTLY* better than format strings[1].

But, cognitively, I find the former much easier to read and write (Apparently many other people seem to as well, although perhaps not everyone). I'm not sure I can explain why especially well, but the visual parsing is just much simpler and more symmetric, and it's much easier to tell at a glance where the full, umm "string", starts and ends, and what's going inside the string and what isn't.

[1] Format strings, even with the new compile-time improvements in the latest DMD, for one thing, they still fail at DRY: Ie, with format strings, even compile-time ones, you're listing your parameters TWICE - once to describe WHERE they go and how they're formatted, and then again SEPARATELY to select WHAT data to be rendered.

And then format strings also have the downside that the "WHAT data" is specified out-of-band, making it making it easy to get things flipped around, and just generally harder to see at-a-glance what's going on (similar to how UFCS chains can be easier to read than a tree of nested function calls, because for UFCS chains the ordering is perfectly sequential, unlike nested function calls and format strings where the logic jumps around).

TBH, I'm not all that excited about the compile-time enhancements to format strings simply because...they're still format strings. And not only that, but they're printf-style syntax which is a total unintuitive mess (C#'s format string syntax was vastly better...for a format string, anyway...)

IMO, the only time a format string should be used instead of std.conv.text() or interpolated strings is when:

1. You're just rendering *one* value at a time with non-standard formatting options (ie, left-/right-justified, leading/trailing zeroes, etc). (Speaking of which, `interp` could really use some formatting features so this could be avoided, and for performance reasons.)

2. You need to support custom formatting specified at runtime (ex: software that supports displaying date/time in custom user-defined formats) but want to be lazy about it and not bother finding/writing a more user-friendly formatting syntax than printf-style (ie, extremely rare).

April 20, 2017
On Thu, Apr 20, 2017 at 03:32:18PM -0400, Nick Sabalausky (Abscissa) via Digitalmars-d wrote:
[...]
> IMO, the only time a format string should be used instead of std.conv.text() or interpolated strings is when:
> 
> 1. You're just rendering *one* value at a time with non-standard formatting options (ie, left-/right-justified, leading/trailing zeroes, etc). (Speaking of which, `interp` could really use some formatting features so this could be avoided, and for performance reasons.)
> 
> 2. You need to support custom formatting specified at runtime (ex: software that supports displaying date/time in custom user-defined formats) but want to be lazy about it and not bother finding/writing a more user-friendly formatting syntax than printf-style (ie, extremely rare).

Hmm. I wonder if this is a matter of habituation and the kind of use cases you most commonly encounter. Having programmed in Perl extensively as well as in C/C++/D, I've dealt with both kinds of syntaxes, and I find that each has its own niche where it does best, while for use cases outside that niche it still works but not as well as the other syntax.

For example, if you are printing lots and lots of text with only the occasional variable, the interpolated syntax is far more readable, e.g.:

	#!/usr/bin/perl
	print <<END
	Dear $title $name,

	This is a spam email sent by $companyName corporation on behalf
	of $evilAdvertisingCompany to solicit for a donation of
	$dollarAmount to the lobbying against anti-spam bills proposed
	by the government of $country.

	Yours truly,
	$spammerName
	END;

is far more readable (and maintainable!) than:

	writefln(q"END
	Dear %s %s,

	This is a spam email sent by %s corporation on behalf
	of %s to solicit for a donation of
	$%d to the lobbying against anti-spam bills proposed
	by the government of %s.

	Yours truly,
	%s
	END", title, name, companyName, evilAdvertisingCompany,
	dollarAmount, country, spammerName);

Much of this is due to the out-of-band issue you mentioned. Somebody could easily write the arguments in the wrong order, or substitute a variable with another one not meant to be formatted, and it would be difficult to notice the mistake just by looking at the code.

But if you're printing lots of variables according to a precise template (e.g., rows of a table or a list of fields), format strings make more sense, e.g.:

	foreach (rec; records) {
		writefln("[%8d] %20s  %10.3f", rec.id, rec.name, rec.amount);
		writefln("      %20s  %10s", rec.altName, rec.comment);
		writefln("      %20s  %6s", rec.address, rec.postalCode);
	}

The advantage here is that you separate formatting from content, ostensibly a good thing depending on which circles you hang out in.

And you can't beat this one with interpolated strings:

	auto matrix = [
		[ 1, 2, 3 ],
		[ 4, 5, 6 ],
		[ 7, 8, 9 ]
	];
	writefln("Matrix:\n%([ %(%3d, %) ]%|\n%)", matrix);

Output:

	Matrix:
	[   1,   2,   3 ]
	[   4,   5,   6 ]
	[   7,   8,   9 ]

If you're doing internationalization, though, neither option is a good one (I gave an example using dates in another post): printf-style formats have ordering issues (is it year first, then month, then day? Or month first then day then year? Which argument is which?), and interpolated strings have the problem of exposing variable names to the translators (who are probably non-coders), potentially opening up the possibility of arbitrary code execution via l10n strings. In this case, it would seem best to have named arguments with format strings.

Between these textbook cases, though, is plenty of gray areas where it's debatable whether one syntax is clearly superior over the other(s). And here, factors of what you're used to, the kind of output you usually need to produce, etc., all come into play and there doesn't seem to be a clear one-size-fits-all.


T

-- 
Why ask rhetorical questions? -- JC
April 20, 2017
On 4/18/17 4:50 AM, Walter Bright wrote:
> On 4/15/2017 4:51 PM, cym13 wrote:
>> Removing imports is a good point, the first concrete one to be
>> mentionned. I'm
>> not sure it matters that much though as I think those imports are
>> generic enough
>> that I believe they would be imported anyway, but it's a real point.
>
> It may not be necessary to have any dependencies on any import.
>
>     $"{a} times 3 is {a*3}"
>
> could be rewritten by the parser to:
>
>     "%s times 3 is %s", a, a * 3
>
> and that is that. (I.e. just an AST rewrite.) It would be quite a bit
> simpler than Jonas' proposed implementation.

Dmitry's solution is superior I think:

$"{a} times 3 is {a * 3}"

->

AliasSeq!(a, " times 3 is ", a * 3)

Would work fine with writeln. Would work fine with format. Wouldn't be a drop-in replacement for a string, as other languages like to do. But has huge potential.

I love Jonathan Marler's idea of using it for SQL inline grammar with prepared statements. Replacing something like:

sql("INSERT INTO table VALUES (?, ?, ?, ?, ?, ?, ?, ?)", a, b, c, d, e, f, g, h);

with:

sql($"INSERT row INTO table VALUES ({a}, {b}, {c}, {d}, {e}, {f}, {g}, {h})");

That is absolutely awesome.

What this does is match the parameter to where it belongs in the string, without all the extra quotes and commas.

And it does no GC activity, and doesn't require any specific formatting tokens!

-Steve
April 20, 2017
On 04/20/2017 04:43 PM, H. S. Teoh via Digitalmars-d wrote:
>
> But if you're printing lots of variables according to a precise template
> (e.g., rows of a table or a list of fields), format strings make more
> sense, e.g.:
>
> 	foreach (rec; records) {
> 		writefln("[%8d] %20s  %10.3f", rec.id, rec.name, rec.amount);
> 		writefln("      %20s  %10s", rec.altName, rec.comment);
> 		writefln("      %20s  %6s", rec.address, rec.postalCode);
> 	}
>

Meh, I'd argue that's just an example of why interpolated strings need support for formatting:

foreach (rec; records) {
    writeln(%"[%8{rec.id}] %20{rec.name   }  %10.3{rec.amount}");
    writeln(%"             %20{rec.altName}  %10{rec.comment}");
    writeln(%"             %20{rec.address}  %6{rec.postalCode}");
}


> And you can't beat this one with interpolated strings:
>
> 	auto matrix = [
> 		[ 1, 2, 3 ],
> 		[ 4, 5, 6 ],
> 		[ 7, 8, 9 ]
> 	];
> 	writefln("Matrix:\n%([ %(%3d, %) ]%|\n%)", matrix);
>

That's actually a perfect example of why I don't like printf-style formatting syntax. I've seen regexes that were more readable.


> If you're doing internationalization, though, neither option is a good
> one (I gave an example using dates in another post): printf-style
> formats have ordering issues (is it year first, then month, then day? Or
> month first then day then year? Which argument is which?), and

There's an extension to printf (which, IIRC, D's format supports) that allows reordering of arguments, much like C#'s format strings, by specifying the argument numerically. But it's an u-g-l-y syntax. Hard to remember, too. And why bother referring to the args numerically when you can have a format string that simply refers to them by name instead (ie, interpolated strings)?


> interpolated strings have the problem of exposing variable names to the
> translators (who are probably non-coders), potentially opening up the
> possibility of arbitrary code execution via l10n strings. In this case,
> it would seem best to have named arguments with format strings.

You're basically describing a templating engine: Which are essentially just interpolated strings with sandboxing (or alternatively, interpolated strings are just embedded templating engines).

And yes, I'd rather use a templating engine for non-coder translators than anything I've seen built-into any programming language: whether format string or interpolated string. But the last thing I'd hand them is anything with printf syntax.


> Between these textbook cases, though, is plenty of gray areas where it's
> debatable whether one syntax is clearly superior over the other(s). And
> here, factors of what you're used to, the kind of output you usually
> need to produce, etc., all come into play and there doesn't seem to be a
> clear one-size-fits-all.

When I see lots of options that are all basically the same thing, but each have their own little limitations and drawbacks (thus limiting each one's applicability to mere subsections of the overall problem domain)...well...that really bugs the shit out of me. ;) It's a big, loud, flashing sign that everybody's designs have gotten things wrong and none of them have quite hit the right mark.

It makes me want to consolidate: Break down the limitations that divide the current options, reach the true "core" of the problem, and show that they're not really all separate tools, but mere approximations of the proper generalization that simply needed to be found and brought out.

It's analogous to mathematical models: When the equations are complex and special-cased (like the rift between Newtonian physics and quantum physics) - that's how they know they probably got the model wrong. When they wind up with something simple, elegant and just-works, that's a sign they may be on the right track. Luckily, good general-purpose tools are a lot easier to come up with than unified field theory. ;) If scientists acted like programmers, the solar system model would still be stuck at the pre-Copernicus "All those planets have VERY COMPLEX movements! Very, VERY complex!! Good thing we have this big ol' toolbox with all the many, many tools involved in modeling all those complexities and special cases!"

Basically, I'm the antithesis of a polyglot programmer :)

April 21, 2017
On Thursday, 20 April 2017 at 18:28:30 UTC, Atila Neves wrote:
> I don't understand how
>
> writeln($"{a} times 3 is {a * 3}");
>
> is even marginally better than
>
> writeln(a, " times 3 is ", a * 3);  // look ma, works right now!
>
> It's not even fewer characters.
>
> Atila

This!

This is bloat that doesn't need adding. D is complicated already, stop adding more 'stuff' and fix what's already there. I wish more time was taken up on libraries and tooling than completely unnecessary features like this.
April 21, 2017
On 04/21/2017 04:11 AM, Gary Willoughby wrote:
> On Thursday, 20 April 2017 at 18:28:30 UTC, Atila Neves wrote:
>> I don't understand how
>>
>> writeln($"{a} times 3 is {a * 3}");
>>
>> is even marginally better than
>>
>> writeln(a, " times 3 is ", a * 3);  // look ma, works right now!
>>
>> It's not even fewer characters.
>>
>> Atila
>
> This!
>
> This is bloat that doesn't need adding. D is complicated already, stop
> adding more 'stuff' and fix what's already there. I wish more time was
> taken up on libraries and tooling than completely unnecessary features
> like this.

"Completely unnecessary" features like that are exactly what make D worthwhile in the first place. Otherwise may as well stick to C++ or Java.