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