Ren-C offers an alternative to historical AJOIN and REJOIN called DELIMIT:
>> delimit "," ["foo" 1 + 2 "bar"]
== "foo,3,bar"
>> delimit/head "," ["foo" 1 + 2 "bar"]
== ",foo,3,bar"
>> delimit/tail "," ["foo" 1 + 2 "bar"]
== "foo,3,bar,"
>> delimit/head/tail "," ["foo" 1 + 2 "bar"]
== ",foo,3,bar,"
If you don't want it to reduce, you can use an @ block:
>> delimit "," @["foo" 1 + 2 "bar"]
== "foo,1,+,2,bar"
SPACED and UNSPACED are specializations, running through the same code for stringifying any non-string contents.
>> spaced ["foo" 1 + 2 "bar"]
== "foo 3 bar"
>> unspaced ["foo" 1 + 2 "bar"]
== "foo3bar"
PRINT uses SPACED internally when you pass it a block. You can use print unspaced to pass it a string if you want unspaced intent.
>> print ["foo" 1 + 2 "bar"]
foo 3 bar
>> print unspaced ["foo" 1 + 2 "bar"]
foo3bar
If you use a character (like #a) instead of a string (like "a"), the delimiting won't be applied. For instance, a newline won't leave spaces on either end of the line break:
>> spaced ["foo" 1 + 2 newline "bar"]
== "foo 3^/bar"
NULLs will trigger errors, VOIDs will vanish.
>> spaced ["foo" if false [1 + 2] "bar"]
== "foo bar"
>> spaced ["foo" pick [a b] 3 "bar"]
** Script Error: non-NULL value required (see MAYBE, TRY, REIFY)
>> spaced ["foo" maybe pick [a b] 3 "bar"]
== "foo bar"