COMPOSE is one of Rebol's most useful and recognizable constructs. As with most-things-Ren-C, it has evolved to become far more powerful.
No Splicing By Default, GROUP! Antiforms Request Splicing
One of the immediately noticeable differences is that there is no /ONLY option any longer. A plain BLOCK! will not splice its evaluative result. So if block: [a b c], it is different from historical Redbol:
rebol2/r3-alpha/red>> compose [value: (block)]
== [value: a b c]
ren-c>> compose [value: (block)]
== [value: [a b c]]
If you want to have something splice, you do this with GROUP! isotopes. For instance, SPREAD produces them:
ren-c>> compose [value: (block), (spread block)]
== [value: [a b c], a b c]
-
You can mix and match splicing parts and non-splicing parts in the same COMPOSE!
-
It gets rid of an instance of /ONLY, which has always been something that causes head-scratching to new users ("only what?")
I am convinced that practice has shown that not splicing is a safer and more intuitive default. You're so often dealing with composing several values at a time that the odds of you knowing precisely what data type all of them are become lesser. There's no question this is the better behavior for people who are writing and reading the code.
The (...) Slots will Vaporize Voids!
In my list of "non-negotiables", I've always said this had to be true, somehow:
>> compose [<a> (if false [<b>]) <c>]
== [<a> <c>]
For quite some time, a conditional like IF that didn't run its branch would return NULL. Because NULL couldn't be put in a block, it seemed like it was a good fit for signaling vaporization of a clause in a COMPOSE. But I was nervous because as NULL came to mean "soft failure", this felt like you could be sweeping a failure under the rug.
But with isotopes, we got a menagerie of states that couldn't be put in blocks (and VOID, with its own antiform). So when un-taken IF provides another choice of VOID vs. NULL, it provided the best of both worlds, where NULL can give a specific error tied to null splicing.
>> compose [<a> (select [a 10 b 20] 'c) <c>]
** Script Error: non-NULL value required (see MAYBE, TRY, REIFY)
>> compose [<a> (maybe select [a 10 b 20] 'c) <c>]
== [<a> <c>]
>> compose [<a> (? select [a 10 b 20] 'c) <c>] ; shorthand for MAYBE
== [<a> <c>]
That specific error can be used to interesting effects, see CURTAIL
Decorated Groups (including quoted) Apply Their Decorations!
Ren-C has a lot of variations of GROUP!:
(set-group!):
:(get-group!)
^(meta-group!)
@(the-group!)
''''(quoted!)
yes, any number of quotes!
COMPOSE does useful magic for you, and if the type a GROUP! evaluates to supports the decoration, it will be applied!
>> fruits: [apple banana]
>> dispositions: [favorite least-favorite]
>> compose [(dispositions.1): '@(second fruits)]
== [favorite: '@banana]
Once you have this, you won't want to go back. The premise of the language is being able to dynamically play with code and generate structures on the fly. This makes that feel extremely seamless.
Change the List Type(s) With COMPOSE2
What if you don't want to use parentheses to point out your compose slots, but brackets?
>> compose2 [] [(1 + 2) [1 + 2] (1 + 2)]
== [(1 + 2) 3 (1 + 2)]
Or what about doubled-parentheses, instead of just one pair?
>> compose2 '(()) [(1 + 2) ((1 + 2)) (1 + 2)]
== [(1 + 2) 3 (1 + 2)]
How about you still want to use groups, but you want to mark them with there being a signal inside the group to show it's a substitution site?
>> compose2 '(<*>) [(1 + 2) (<*> 1 + 2) (1 + 2)]
== [(1 + 2) 3 (1 + 2)]
Whoa!
Predicate Functions Can Process the Compose Slots!
We now have the ability to run functions on the groups before you splice them.
So if you want to define COMPOSE that acts historically like Rebol2 (splicing unless you say :ONLY), here's one way you can do it:
old-compose: adapt augment compose/ [:only] [
if not only [
predicate: func [group <local> product] [
either any-array? product: eval group [spread product] [:product]
]
]
]
We added the :ONLY refinement, and if you don't use it then it adds a processing function for arrays. This gives you the historical result!
This only scratches the surface of what's possible, with these bendable and useful ergonomics.