Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR is technically a part of our Format effort, but:
Intro
String interpolation is used in most languages as a readable way of inserting values into a string.
Though the first question everyone of us may have is "what, another
rejoin
?", ifrejoin
was good enough for me or Gregg, this design would never have been born.Here are just a few common examples written using
rejoin
function:Try to look at these expressions and visualize how the resulting string will look like, and if I've got all spaces and quotes right.
Not a human task, eh? Even telling the strings from code requires syntax highlighting or quote counting or guessing.
These are equivalent expressions written via string interpolation:
Pretty clear, right?
Another, even more solid reason for this design is translation. Whatever way we choose to translate our software, we have to give whole messages to the translator.
For more background info you can read related Format discussion, preliminary design document and the original sad-emoji dialect design, but I'll try to explain the main points here.
List of so far registered use cases can be found here but those are only mine. Maybe Gregg can find his own somewhere. Below that is a description of design implemented in this PR - a result of my experience with interpolation.
Why a macro?
Since input is a string, we don't get
word!
s from it. Without words, nothing is bound during context and function creation. Then the moment we extract words from the string, we can only bind them to the global scope, or to an explicitly provided list of contexts. Gabriele expressed the concern very clearlyMacro helps us avoid this trap. It gets expanded and produces words before those words are bound, resulting in code that just works.
We also get the benefit of speed: if program is compiled, all macros are expanded at compile-time.
Function approach is still provided for advanced users as it has it's merit: macro cannot work on message that is generated at run time (it can, but it has no knowledge of contexts where to bind words).
Syntax
Every paren inside the string becomes code:
#rejoin "(x) + (y) = (x + y)"
->rejoin ["" (x) " + " (y) " = " (x + y)]
Why parenthesis? Because as everywhere else in Red, it visually hints at evaluation.
To treat a paren literally, it is escaped inside by a backslash:
#rejoin "total (n) hits (\ratio: (100% * n / total))"
->rejoin ["total " (n) " hits (ratio: " (100% * n / total) ")"]
This relies on backslash being invalid in Red, so if there's a plan to leverage
\
then another escape sigil must be found.Original string type is preserved (leading
""
also serves this purpose):#rejoin %"file-(n).(ext)"
->rejoin [%"file-" (n) "." (ext)]
#rejoin <x (y)=(z)>
->rejoin [<x > (y) "=" (z)]
Extensions
Format module will also include a
#format
macro, backward-compatible with#rejoin
but with two more features:(expression as mask)
into(format (expression) mask)
#format/in "you've spent (spent as {$0.00}) of (spent + left as {$0.00})" locale
will be preprocessed into
#rejoin "you've spent (format/in (spent) {$0.00} locale) of (format/in (spent + left) {$0.00} locale)"
This will require Issues in paths are not lexed #5009 to be solved.
It is expected that users will write their own macros for common cases based on
#rejoin
, e.g.:Another extension idea concerns
#error
macro. Right now the only fully custom error we have is the User Error. I propose adding another fully custom error into every error category, so we could produce Script, Math, other errors with custom messages.#error
should then default to Script error, with#error/math
,#error/syntax
,#error/user
etc forms changing the error type.For cases when template is only known at runtime (e.g. report generation with user-defined template),
rejoin
function is extended to acceptany-string!
argument of exactly the same syntax as the macro:rejoin <img src=(url) size=(as-pair sizex sizey)>
-><img src=http://../image.png size=100x100>
rejoin/with
accepts one or more contexts to bind produced expressions before reducing themrejoin/trap
allows one to replace evaluation errors with some text (per original Gregg's design)/with
and/trap
only apply to string case and have no effect on block argumentWISH: URLs to support parens
()
for string interpolation REP#112 should be considered to bring URL support to this design.P.S. I need some help reducing the docstrings :)