It's somewhat fundamental in Rebol that people have been driven by the types of things to do dialecting.
But it has always been a little hard when you want to transfer properties that are sort of "meta-type". For instance: how would you do this in Rebol2?
rebol2>> map-each item [word pa/th] [<??? what code goes here ???>]
== [:word :pa/th]
-
You can turn a WORD! into a GET-WORD! with
to get-word! -
You can turn a PATH! into a GET-PATH! with
to get-path! -
...but how do you turn something that might be a WORD! or a PATH! into a GET-WORD! or a GET-PATH! as appropriate?
![]()
Ren-C's More Uniform Substrate...
I've reimagined the ideas of what used to be things like GET-WORD! or GET-PATH! into a generalized concept of CHAIN!s, which can have a SPACE in their first slot.
This ushered in new composition possibilities without bringing in a wave of new types. :(a) isn't a GET-GROUP!, it's just a CHAIN! with a space in its first slot, and a GROUP! in the second. And COMPOSE got smart about these things:
>> word: 'foo
>> compose ':(word)
== :foo
This would appear to give us the answer to our question about how to turn something that's either a WORD! or a TUPLE! into a version with a leading colon (for reasons of hierarchy, you can't put PATHs in CHAINs, but under the evaluator logic of what paths and chains are for you wouldn't want to, you'd put a tuple in...)
>> map-each item [word tu.p.le] [compose ':(item)]
== [:word :tu.p.le]
That particular case looks pretty good. But we have a little bit of a puzzle now with things that used to be easy that became hard.
For example, let's talk about the change of things like /foo from being REFINEMENT! to being a PATH!. You used to be able to TO WORD! on a REFINEMENT! or vice-versa, but under the new rules you can't... so how to do this:
>> map-each item [@word :tu.p.le] [<??? what code goes here ???>]
== [/word /tu.p.le]
This gets a little sticky. Of course you have all the machinery in your hand to write it (just as you could have always written a Rebol2 to-getlike that would accept either a WORD! or PATH! and make a GET-WORD! or GET-TUPLE!). But that's kind of unsatisfying...we want something easy.
Meet the DECORATION Functions!
Decoration is a concept that subsumes:
- Quoting
- Sigils
- Leading-Space Sequences
You can ask a value for its decoration, and if it has any of the above you get a composite from that. For instance, if you ask for the decoration of a CHAIN! with a space in its first position, you'll get the special WORD! of : as a proxy representative of the chain interstitial:
>> d: decoration of the :tu.p.le
== :
Then you can take that decoration, and apply it to something else:
>> decorate d [a b c]
== :[a b c]
And you can remove decorations as well:
>> undecorate the :tu.p.le
== tu.p.le
It generalizes to where quoting is considered a decoration too:
>> decoration of the 'foo
== '
>> decoration of the ''bar
== ''
So are Sigils.
>> decoration of the $tie
== $
>> decoration of the ^meta
== ^
>> decoration of the @pin
== @
(Those are quoted and sigil'd SPACE respectively...these kinds of cases are what motivate why the underscore disappears when you decorate a SPACE rune.)
So this means you can generalize a dialect which wants to be parameterized--e.g. by letting you pass in what decoration it wants to use--and not be concerned about whether that decoration is implemented by quoting, or sigils, or a sequence with its interstitial in the leading position due to a space in the first slot.
You can even grab composite decorations and manipulate them easily:
>> d: decoration of the ''@/foobar
== ''@/
>> d: noquote d
== @/
>> pinned? d
== \~okay~\ ; antiform
>> d: unpin d
== /
>> meta d
== ^/
>> decorate (meta d) [x y z]
== ^/[x y z]
There's handling for when decorating doesn't merely create a 2-element sequence, but when the decoration indicates the same type as what its decorating so the sequence just gets longer...
>> decoration of the .member
== .
>> decorate '. the member.submember
== .member.submember ; merges sequence if same type
(I'll mention that having .member act like pick . member, and using . as the "self" or "this" in objects, is working out really well...especially since when you're not in a member function you can define dot to anything you want and still use the shorthand!)
So We Have An Answer To The Puzzle
I asked above how to turn [@word :tu.p.le] into [/word /tuple].
Now we can:
>> map-each item [@word :tu.p.le] [decorate '/ undecorate item]
== [/word /tu.p.le]
And a shortened helper that does UNDECORATE followed by DECORATE is of course called REDECORATE.
>> map-each item [@word :tu.p.le] [redecorate '/ item]
== [/word /tu.p.le]
![]()
Added Bonus: Applies Decorations From A PARAMETER!
The first iteration of DECORATE was made to let you reconstitute a function spec parameter from the parameter itself. Let's say if something was a refinement:
>> append.dup
== &[parameter! :[any-number? pair!]]
Although you know that in the spec of APPEND the DUP has a colon as :DUP, here you see the decoration mechanic was used to transfer that onto the BLOCK! (because it's a parameter property, not a property of the name which is in the key of the frame).
You can do this yourself:
>> decorate 'dup append.dup
== :dup
>> append.dup.spec
== [any-number? pair!]
>> decorate append.dup append.dup.spec
== :[any-number? pair!]
See this post for applications and explanations:
https://rebol.metaeducation.com/t/how-to-get-spec-of-or-parameters-of-functions/2578
![]()