April 19, 2017
On Wednesday, 19 April 2017 at 15:07:55 UTC, Jonas Drewsen wrote:
> I'm talking about building format strings just yet... I'm just working with the suggestion that Walter brought up with converting the interpolated string into something that can be fed into format e.g.:
>
> $"The date is {%04d year} and {user} just logged into {here}"
>
> is rewritten by the compiler to:
>
> "The date is %04d and %s just logged into %s", year, user, here
>
> which can be fed into for example format(). Not sure I like the need to call format to get the resulting string, but just working with the idea here.
>
> I also think it would loose a lot of value to only allow strings as you suggest (e.g. %dateString).

If we had language-level tuple literals you could desugar the expression into:

("The date is %04d and %s just logged into %s", year, user, here)

And let a user do whatever they want with it.

Even now it can be done with an AliasSeq, but not with the automatic insertion of format arguments, of course.

alias fs = AliasSeq!("The date is %04d and %s just logged into %s", 1992, "Alan", "10.1.0.123");

writeln(format(fs)); //Prints "The date is 1992 and Alan just logged into 10.1.0.123"


April 19, 2017
On Tue, Apr 18, 2017 at 05:30:31PM -0700, Walter Bright via Digitalmars-d wrote: [...]
> Reminds me of a story from the 1980s. Microsoft's MASM stood for "Macro Assembler". Inevitably, Microsoft programmers invented a pile of macros that sort of turned asm programming into a pseudo-high-level language. This was shipped with every copy of MASM.
> 
> A friend of mine who worked at MS was once given the task of fixing a bug in 50K of MASM code written in this macro language. The author of it had long since moved on. Every coder assigned to this task failed. My friend got it fixed in a couple hours. He was asked by his astonished manager how he'd managed to do it:
> 
> "I assembled the code into an object file. Then I disassembled it with Zortech's OBJ2ASM, figured out how to fix it, then submitted the disassembled code as the new source code."

+1, Classic!


T

-- 
VI = Visual Irritation
April 19, 2017
On Wednesday, 19 April 2017 at 13:04:08 UTC, Jonathan Marler wrote:
> On Wednesday, 19 April 2017 at 12:03:47 UTC, Stefan Koch wrote:
>> On Wednesday, 19 April 2017 at 11:59:51 UTC, Jonas Drewsen wrote:
>>
> I can think of 3 reasons.
>
> 1. Requires GC.
>
> NOTE: I believe that most applcations should use GC memory, that being said, library code has to be nogc wherever it can if it wants to be used by nogc apps, so this solution is unusable in alot of library code.
>
> 2. It's verbose.  Jonas provided a good example of why and he makes a good point that your example only has 1 formatted argument and this problem gets much worse with multiple.
>
> 3. The biggest reason I wouldn't use this solution because it uses string composition.  String composition wastes memory and memory management cycles.  And when I say "waste" what I mean is, the use is unnecessary.  In general composition is a powerful tool because it allows you to easily overload and abstract and add new functionality, however, it requires runtime overhead.  This is the reason that the toString(delegate) was created to replace the composable toString paradigm.  It's also the reason that string formatting exists at all.
>
> To summarize:
>
> // requires GC, too verbose, uses string composition which wastes heap resources
> writeln("The date is " ~ format("%04d", year));
>
> // efficient, but format string and args can get out of sync.
> writefln("the date is %04d", year);
>
> //
> writefln($"the date is {year:04d}");
>
> If string interpolation gets reduced to the 2nd case, then it will have the same efficiency and solve a problem.  Whether that problem justifies the change is up for debate, but I think it *might be*.

As a string interpolation sceptic I have to admit that I found one application of that concept that is probably much better than the current C derived format strings: Internationalisation.

    dates["en"] = "The date is %2$02d-%1$02d-%3$04d";
    dates["fr"] = "La date est %1$02d-%2$02d-%3$04d";

    writeln(format(dates[lan], day, month, year));

with interpolation we have

    dates["en"] = $"The date is {month:%02d}-{day:02d}-{year:04d}";
    dates["fr"] = $"La date est {day:%02d}/{month:%02d}/{year:04d}";

which is a little bit easier to read than positional format specifiers, which have the added backdraw (at least in C, I don't know for D) that they are an none or ALL thing. When you need them, you have to put them an all format specifiers.
This said, the interpolation has in that case a big drawback, i.e. that they "export" the variable names outside of the scope they are defined in. That's a thing that is often required for I10N, that the strings are defined in a separate file or module and selectively imported (at runtime).
April 19, 2017
On Wed, Apr 19, 2017 at 06:38:13PM +0000, Patrick Schluter via Digitalmars-d wrote: [...]
> As a string interpolation sceptic I have to admit that I found one application of that concept that is probably much better than the current C derived format strings: Internationalisation.
> 
>     dates["en"] = "The date is %2$02d-%1$02d-%3$04d";
>     dates["fr"] = "La date est %1$02d-%2$02d-%3$04d";
> 
>     writeln(format(dates[lan], day, month, year));
> 
> with interpolation we have
> 
>     dates["en"] = $"The date is {month:%02d}-{day:02d}-{year:04d}";
>     dates["fr"] = $"La date est {day:%02d}/{month:%02d}/{year:04d}";
> 
> which is a little bit easier to read than positional format specifiers,

Somebody proposed some years ago to add named parameter support to format strings (if not to D itself). So ostensibly, instead of writing the nastiness like the above "La date est %1$02d...", you'd be able to write something like:

     dates["en"] = "The date is %{month}02d-%{day}02d-%{year}04d";
     dates["fr"] = "La date est %{day}02d-%{month}02d-%{year}04d";

     writefln(dates[lan], [ "day": day, "month": month, "year": year]);


> which have the added backdraw (at least in C, I don't know for D) that they are an none or ALL thing. When you need them, you have to put them an all format specifiers.

I think std.format.format actually allows you to mix positional and non-positional specifiers in the same format string. But the semantics have some tricky bits, though, so I wouldn't recommend doing it.


> This said, the interpolation has in that case a big drawback, i.e. that they "export" the variable names outside of the scope they are defined in. That's a thing that is often required for I10N, that the strings are defined in a separate file or module and selectively imported (at runtime).

With named parameter support in format strings, we wouldn't have this problem, since you could name the parameters however you want regardless of actual variable names. You could even perform arbitrary computations on the variables before passing it to format, e.g.:

	writefln("The date is %{year}04d-%{month}02d-%{day}02d", [
		"year": yearsSinceEpoch + 1970,
		"month": zeroBasedMonth + 1,
		"day": zeroBasedDay + 1
	]);

This way, implementation details like zero-based counting, years since the Unix Epoch, etc., are kept within the code where it belongs, not in the i18n strings.

I'd argue that interpolated strings have a disadvantage here, because you wouldn't want to expose the computation `yearsSinceEpoch + 1970` to the l10n translators, who may inadvertently modify the expression to something wrong. Plus, allowing arbitrary (Turing-complete!) expressions inside l10n strings is just a security exploit waiting to happen.


T

-- 
Bare foot: (n.) A device for locating thumb tacks on the floor.
April 19, 2017
On 4/19/2017 5:04 AM, Jonas Drewsen wrote:
> On Tuesday, 18 April 2017 at 08:42:38 UTC, Walter Bright wrote:
>> On 4/15/2017 1:04 PM, Jonas Drewsen wrote:
>>> [...]
>>
>> Thanks for doing the work to make a sample implementation, too. I don't know
>> if this will make it into D, but Jonas is a fine example of a champion.
>
> Thanks for the feedback. Nice to know that it is not immediately off the table
> at least :)
>

I can't promise anything. But it does need a lot of work. A survey of how it is done in other languages is helpful to see what they find useful and to see what has been overlooked. A quick look by me shows a lot of variety.

D's lambda syntax came about after such a survey, and we cherry-picked the best characteristics of the lot. The result was a nice home run for D.

The n.g. discussion also brought up a couple of existing library solutions. Those need to be evaluated as well and compared with any language solution.
April 19, 2017
I forgot to mention - the pros and cons of whether the string interpolation is compile time or run time is a critical decision.
April 20, 2017
On Wednesday, 19 April 2017 at 17:51:05 UTC, Nick Sabalausky (Abscissa) wrote:
> On 04/17/2017 03:41 PM, Jonas Drewsen wrote:
[...]
>>>    exho!"The number ${num} doubled is ${num * 2}!"
> Also, it only works if you're just sending the string to writeln. It doesn't help the general case :(
you can define:

  auto mixinter(string x)(){return mixin(interp!x);}

(in the same scope as the vars used inside x)
 And use:

  string text = mixinter!"${name} and this are app ${age*365*24} hours!";


But can someone explain me why this works:

import scriptlike;
void main()
{

  auto name = userInput!string("Please enter your name");
  auto age = userInput!int("And your age");

  // The proposed notation for scriplike string interpolation

  writeln(mixin(interp!"${name} you are app. ${age*365} days old"));

  // with exho definition
  auto exho(string x)(){return mixin("writeln("~interp!x~")");}

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


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!";
}

Can it be possible to allow a function to be defined outside the scope of use to return a "mixin object"?, than everything can be done in a lib outside, no need to add parsing complexity to the language?





April 20, 2017
On Thursday, 20 April 2017 at 10:23:30 UTC, Martin Tschierschke wrote:
> Can it be possible to allow a function to be defined outside the scope of use to return a "mixin object"?

That's basically what I propose: https://forum.dlang.org/post/msotbcaqipiiqxiuppnj@forum.dlang.org this can be cheap compromise between macros and mixins.
April 20, 2017
On 2017-04-19 03:45, Jon Degenhardt wrote:
> On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
>>   I've been wanting to have support for interpolated strings in D for some time now that will allow you to write e.g.:
>> [...]
> 
> One place I'd appreciate interpolated strings is as an option when working with heredoc strings. These strings are often multiple lines or paragraphs, using format style construction is often error-prone due to length.
> 
> --Jon

So, that makes 2 otherwise error-prone use-cases.
The first being keeping arguments & format string in sync (Jonathan Marler).
And they actually compound! (Large strings get out of sync quicker I would guess)


And, I would like to add that this is are very newby friendly feature.
Nowadays people might even expect a language to have this.
April 20, 2017
On Monday, 17 April 2017 at 19:38:33 UTC, Kapps wrote:
> On Saturday, 15 April 2017 at 20:04:13 UTC, Jonas Drewsen wrote:
>> [...]
>
> C# got this feature recently. I didn't expect it to be a significant difference, but I do find it a substantial improvement. Not only does it clearly show what goes where, but it's so much cleaner and more convenient.

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