Usefulness of String Interpolation

To pick a random example from the build helpers for "CScape" interpolation of some generated C code:

emit {
    #define ${MAYBE PREFIX}INCLUDE_PARAMS_OF_${NATIVE-NAME} \
        $[Items]; \
        assert(Get_Series_Info(level_->varlist, HOLD))
}
  • The use of ${} (instead of $() or $<>) means that the result of the expression should be turned into a valid C identifier name... so dashes are converted to underscores, etc.

  • The use of all capitals in the ${} escaping means that the strings generated by the expressions evaluated should be made all uppercase.

  • The use of $[] means that items is an array, and its elements should be printed one line at a time...repeating the boilerplate leading and trailing on each line (in this case an indent on the left, and a semicolon and backslash on the right)

The template looks something like the result:

#define INCLUDE_PARAMS_OF_IF \
    DECLARE_PARAM(1, return); \
    USED(ARG(return)); \
    DECLARE_PARAM(2, condition); \
    DECLARE_PARAM(3, branch); \
    assert(Get_Series_Info(level_->varlist, HOLD))

Without interpolation, we fall back on LOAD-able code... where spaces and quotes are required by the language itself. This starts to lose the ability to keep track of actual spaces in the interpolated thing, plus you keep having to start and stop string delimiters on the string portions.

I'm not quite sure how it would come together dialected via regular code, but it would drift away from looking like C code, at best it might look like:

emit [
    "#define " <c> (MAYBE PREFIX) "INCLUDE_PARAMS_OF_" <c> (NATIVE-NAME) " \"
    "    " @[Items] "; \"
    "    assert(Get_Series_Info(level_->varlist, HOLD))"
]

I'd be hard-pressed to say the spacing was correct on inspection. We've lost the intuition about where the unspaced parts are. You can imagine it getting worse when you're building unspaced material inside a string literal. Strings can simply be the least noisy medium when you want to see something that looks close to the result.

Anyway, with strings carrying binding, we wouldn't have to do what we do today... which is actually pass the variables (that don't live in LIB) in a block to emit:

emit [prefix native-name items] {  ; <-- ack
    #define ${MAYBE PREFIX}INCLUDE_PARAMS_OF_${NATIVE-NAME} \
        $[Items]; \
        assert(Get_Series_Info(level_->varlist, HOLD))
}

So I look forward to getting rid of that.

Note that Ren-C has DELIMIT (and UNSPACED, SPACED) instead of AJOIN... which hopefully you'll like even better.

1 Like

OK, this is a lot more powerful than the string interpolation I’m used to. I can see why you’d want this — it fits in very well with the general idea of dialecting.

(Personally, I’m not at all averse to templating via concatenation, as in this code of mine from two days ago. But Haskell isn’t Rebol.)

1 Like

Reminds me of StringTemplate - which I have never used, but thought was interesting.