The state of string interpolation

Jonathan Marler johnnymarler at gmail.com
Thu Dec 6 22:31:07 UTC 2018


On Thursday, 6 December 2018 at 21:47:02 UTC, Sebastiaan Koppe 
wrote:
> On Thursday, 6 December 2018 at 21:20:54 UTC, Mike Franklin 
> wrote:
>> 1) What is the fundamental missing language feature that is 
>> preventing us from implementing interpolated strings in the 
>> library?
>
> To access symbols from the caller's scope.
>
>> 2) What other use cases besides string interpolation would be 
>> enabled by adding such a feature?
>
> Any thing from scala's implicit playbook.

There's a reason why there's no way to access the callers scope 
without a mixin...because it breaks encapsulation.  Imagine this:

int x = 10;
foo();


What is the value of x after this?  If a function (or whatever 
foo is) could access the caller's scope, then it could be 
anything.  This adds a very large mental burden on the reader 
because now every function call could modify any local state at 
any point.  That's why you need this:

int x = 10;
mixin foo();

Now you've got a flag to indicate that you need to understand foo 
before you can make any assumptions about your local state.  
There's a balance between power and reasonable encapsulation.  If 
you make functions too powerful, then understanding code becomes 
a nightmare.

Interpolated strings are a special case because even though they 
can access the caller's scope, they do this explicitly:

int x = 10;
foo(i"$(x=20)");

You can clearly see that x is being set to 20, you don't need to 
know the implementation specifics of interpolated strings to see 
that.

A feature that allows functions and/or templates to access the 
caller's state implicitly would allow us to implement 
interpolated strings, however, this would come at the cost of 
adding a very large mental burden to understand any and all D 
code going forward.

On that note, you could make an argument that you should be able 
to access the caller's state in a "read-only" capacity, but that 
can get complicated.  Say you have this:

int x = 10;
int* y = &x;
foo();

What's the value of x after this?  What if we define foo as:

void foo(CallerScope s = __CALLER_SCOPE__)
{
     *(cast(int*)s.y) = 20;
}

We did have to cast `y` from const(int*) to int*, but we were 
able to change x nonetheless. You end up with the same problem 
that you have to understand every function you call at a much 
deeper level to know when it can modify your local state.


More information about the Digitalmars-d mailing list