December 06, 2018
On Thursday, 6 December 2018 at 20:00:02 UTC, H. S. Teoh wrote:
> Essentially, what you want to be able to do is essentially for a template to be able to reference symbols in the caller's scope, rather than in the template's scope.

Am I missing something? D has this already in the form of mixin templates.
December 06, 2018
On Thursday, 6 December 2018 at 20:45:57 UTC, Meta wrote:
> On Thursday, 6 December 2018 at 20:00:02 UTC, H. S. Teoh wrote:
>> Essentially, what you want to be able to do is essentially for a template to be able to reference symbols in the caller's scope, rather than in the template's scope.
>
> Am I missing something? D has this already in the form of mixin templates.

Mixin templates can *add* symbols to the caller's scope, but they can't refer to *existing* symbols unless they're passed in as template parameters.
December 06, 2018
On Thursday, 6 December 2018 at 20:49:08 UTC, Paul Backus wrote:
> On Thursday, 6 December 2018 at 20:45:57 UTC, Meta wrote:
>> On Thursday, 6 December 2018 at 20:00:02 UTC, H. S. Teoh wrote:
>>> Essentially, what you want to be able to do is essentially for a template to be able to reference symbols in the caller's scope, rather than in the template's scope.
>>
>> Am I missing something? D has this already in the form of mixin templates.
>
> Mixin templates can *add* symbols to the caller's scope, but they can't refer to *existing* symbols unless they're passed in as template parameters.

mixin template myTpl()
{
    pragma(msg, "name: ", typeof(name));
}

void main()
{
    auto name = "abc";
    mixin myTpl!(); //Prints "name: string"
}

But you do have to know the name of the symbol in advance. I assume that's what you mean by "they can't refer to *existing* symbols unless they're passed in as template parameters".
December 06, 2018
On 12/6/18 3:08 PM, Steven Schveighoffer wrote:
> On 12/6/18 2:52 PM, Adam D. Ruppe wrote:
> 
>> Passing the actual variable as a CT arg is doable.. but only if there IS an actual variable.
>>
>> i"hi $(name)"; // could pass T!("hi ", name)
>> i"hi $(a+b)"; // error: cannot read variables at compile time
> 
> Yeah, I know. I was hoping we could bend the rules a bit in this case. Since an interpolation would be a different kind of alias (with special traits attached), it could have its own allowances that normally don't happen. Make it like a lazy alias ;)

This doesn't work:

foo!(a + b);

But this does:

foo!(() => a + b);

However, the type is different, and it's not implicitly called, as it's a lambda.

So writeln(Args) would show "hi <someFunctionSignature>".

We still need some compiler-magic here, but it's almost supported...

-Steve
December 06, 2018
On 12/6/18 3:00 PM, H. S. Teoh wrote:
> On Thu, Dec 06, 2018 at 02:27:05PM -0500, Steven Schveighoffer via Digitalmars-d wrote:
>> On 12/6/18 2:12 PM, Paul Backus wrote:
>>> Marler's original proposal is simple, orthogonal, and elegant. It
>>> makes use of existing D features in natural ways, and is both easy
>>> to understand, and easy to use in simple cases without *needing* to
>>> understand it. I think adding additional machinery like
>>> FromInterpolation on top of it would be a mistake. If users want to
>>> opt in to that extra complexity, it can always be made available as
>>> a library.
> [...]
>> The more I think about it, the better it is to use the original
>> proposal, and just pass the parameters at compile time in order to
>> make it work. The non-string portions will be aliases to the
>> expressions or variables, making them easily distinguishable from the
>> string portions.
>>
>> So instead of my original db.exec(...), you'd do db.exec!(...),
>> exactly the same as before, just it's passed at compile time vs.
>> runtime. The runtime possibility is there too, but you wouldn't use it
>> in a database setting.
>>
>> This can be used to forward to whatever you want. If you want to
>> change the parameters to FromInterpolation("foo", foo), it will be
>> possible.
>>
>> This is very D-ish, too, where we supply the compile-time mechanisms,
>> and you come up with the cool way to deal with them.
> [...]
> 
> Yes, this.  I support this.
> 
> Now, I'm also thinking about how to minimize the "surface area" of
> language change in order to make this proposal more likely to be
> accepted by W&A.  Because as it stands, the exact syntax of interpolated
> strings is probably going to ignite a bikeshedding war over nitty-gritty
> details that will drown the core proposal in less important issues.
> 
> While it's clear that *some* language support will be needed, since
> otherwise we have no way of accessing the surrounding scope of the
> interpolated string, I wonder how much of the implementation can be
> pushed to library code (which would make this more attractive to W&A),
> and what are the bare essentials required, in terms of language change,
> to make a library implementation possible.

Why do we need support for accessing the surrounding scope? The passed-in variable aliases and expressions should be all that is needed.

[snip]

> Just throwing out some ideas to see if there's a way to do this without
> making a big change to the language that will likely be rejected.

I don't think we need to start with such a drastic addition. Let's start with lowering the interpolation string to the tuple, and see how far we can get from that.

-Steve
December 06, 2018
On Thu, 06 Dec 2018 20:45:57 +0000, Meta wrote:
> On Thursday, 6 December 2018 at 20:00:02 UTC, H. S. Teoh wrote:
>> Essentially, what you want to be able to do is essentially for a template to be able to reference symbols in the caller's scope, rather than in the template's scope.
> 
> Am I missing something? D has this already in the form of mixin templates.

From the spec:

"A TemplateMixin can occur in declaration lists of modules, classes, structs, unions, and as a statement."

Also, it still requires the `mixin` keyword at the place of instantiation.

So you want to write:

    writeln(interp!"Hi, ${user.name}!");

But you currently have to write:

    mixin interp!"Hi, ${user.name}!";
    writeln(interpResult);
December 06, 2018
On Thu, 06 Dec 2018 20:49:08 +0000, Paul Backus wrote:
> Mixin templates can *add* symbols to the caller's scope, but they can't refer to *existing* symbols unless they're passed in as template parameters.

A mixin template is declared as `mixin template Foo() {}`. This form of template can refer to symbols in the instantiating scope. For instance:

mixin template Foo()
{
    auto b = a;
}

void main()
{
    int a = 12;
    mixin Foo;
    writeln(b);
}

All templates can be mixed in, but if it wasn't declared as `mixin template`, it only has access to symbols in the declaration scope.
December 06, 2018
On Thursday, 6 December 2018 at 20:00:02 UTC, H. S. Teoh wrote:
> Now, I'm also thinking about how to minimize the "surface area" of language change in order to make this proposal more likely to be accepted by W&A.

IMO the one thing that will likely get this across the finish line is to generalize it:

1) What is the fundamental missing language feature that is preventing us from implementing interpolated strings in the library?

2) What other use cases besides string interpolation would be enabled by adding such a feature?

If a compelling language feature that has more uses beyond string interpolation can be engineered and/or discovered, I think it will have a much better chance of being accepted.

Mike
December 06, 2018
On Thu, Dec 06, 2018 at 09:06:16PM +0000, Neia Neutuladh via Digitalmars-d wrote:
> On Thu, 06 Dec 2018 20:45:57 +0000, Meta wrote:
> > On Thursday, 6 December 2018 at 20:00:02 UTC, H. S. Teoh wrote:
> >> Essentially, what you want to be able to do is essentially for a template to be able to reference symbols in the caller's scope, rather than in the template's scope.
> > 
> > Am I missing something? D has this already in the form of mixin templates.
> 
> >From the spec:
> 
> "A TemplateMixin can occur in declaration lists of modules, classes, structs, unions, and as a statement."
> 
> Also, it still requires the `mixin` keyword at the place of instantiation.
> 
> So you want to write:
> 
>     writeln(interp!"Hi, ${user.name}!");
> 
> But you currently have to write:
> 
>     mixin interp!"Hi, ${user.name}!";
>     writeln(interpResult);

Ah, so mixin templates *can* access symbols from the containing scope. That's nice... so actually, we can already implement string interpolation in the library.  The only complaint is the ugly syntax required `mixin blah!"abc ${def}"` instead of simply `blah!"abc ${def}"` or `blah("abc ${def}")`.

I'm in favor of making the syntax less ugly, but I fear Andrei's objection is going to be that (1) this is already possible without a language change, and (2) the proposed language change would effectively only add syntactic sugar, which is unlikely to be enough justification for doing it.

The above syntax *could* possibly be made just slightly less ugly, if we extended mixin templates to allow mixin *expressions*, e.g.:

	writeln(mixin interp!"Hi, ${user.name}!");

instead of needing to break it into two lines and accessing an implicit symbol. Having to break it into two lines is really a serious blow to writability / usability.  I'd like to get rid of the `mixin` as well, but I have a feeling Andrei isn't going to like that.

Or perhaps, one could employ the alternative:

	mixin interpFun!(writeln, "Hi, ${user.name}");

where the interpFun template essentially implements the function call on your behalf.  This is essentially a hack, though, and wouldn't be easily extendible to the general case without further contortions.  Extending mixin templates to allow expressions seems to be the least undesirable option, barring actually adding string interpolation support to the compiler / language spec.


T

-- 
Don't get stuck in a closet---wear yourself out.
December 06, 2018
On Thu, Dec 06, 2018 at 09:20:54PM +0000, Mike Franklin via Digitalmars-d wrote:
> On Thursday, 6 December 2018 at 20:00:02 UTC, H. S. Teoh wrote:
> > Now, I'm also thinking about how to minimize the "surface area" of language change in order to make this proposal more likely to be accepted by W&A.
> 
> IMO the one thing that will likely get this across the finish line is to generalize it:
> 
> 1) What is the fundamental missing language feature that is preventing us from implementing interpolated strings in the library?
> 
> 2) What other use cases besides string interpolation would be enabled by adding such a feature?
> 
> If a compelling language feature that has more uses beyond string interpolation can be engineered and/or discovered, I think it will have a much better chance of being accepted.
[...]

Thanks for stating what I had in mind in a much clearer way than I did. :D

After thinking about it more, I realize we do have mixin templates that are able to access symbols in the surrounding scope already. So, at least in principle, we could already implement string interpolation in the library, e.g.:

	int i, j;
	string s, t;
	mixin interpolate!"${s} owes ${i} favors and ${j} free tickets to ${t}";
	writeln(interpolateResult);

or something along similar lines.

The only real objection I can see is the ugliness of the syntax, since currently mixin templates can only be used in statements, rather than expressions, so to pass multiple interpolated strings to a function, you'd need a bunch of ugly assignments and temporaries on multiple lines, thus diminishing the usability of the feature.

If syntactic sugar is the only essential ingredient, then I'm not seeing a high chance of this proposal being accepted.

But OTOH, making mixin templates usable as expressions, not just statements, extends beyond mere syntactic niceness. It would allow a wider application of mixin templates to be usable as smaller chunks of a larger expression, that otherwise would require onerous amounts of boilerplate to express.  It's also arguably a natural extension of mixin templates as an existing language feature (i.e., we already have mixin template statements, the logical next step is mixin template expressions). So that could possibly be the angle we should push for.


T

-- 
"Hi." "'Lo."