Hmm, well, now that we've gone a little further in the virtual binding design... how about this idea...
What if the currency of string interpolation is just a string in a BLOCK! ?.
internals: func [a <local> b] [
b: "internal-B"
let c: "internal-C"
print interpolate ["$(a) $(b) $(c)"] ; string wrapped in BLOCK!
]
>> internals "argument-A"
argument-A internal-B internal-C
Block evaluation captures the "current" evaluation environment. Then interpolate can ask that block to do lookups, as if it were a context (get in block 'a)
It's a couple of extra characters. But this would mean you wouldn't have to fret about using/exchanging string literals and having them carry the burden of environments. A lot better than starting to worry about having to quote your strings to suppress binding!
Because typechecking is done with predicates, we can typecheck "string-in-block" now, so that makes things a little nicer than saying the interpolate function just takes a BLOCK!. Interpolatable strings can be their own datatype without having to come up with a new DATATYPE!
Moreover, I imagine it's not unusual to want to have more information in interpolation scenarios than just the string anyway, so having a block might just come for free in a lot of cases where it's part of a dialect.
(Blocks capturing environments still have the potential to create a lot of waste, and that needs to be addressed. But at least this pares down the concern a bit...)