...and by *"it's time" I apparently meant "within the next year, maybe"...
But better late than never, right? It's in!
Refinements Can be Provided In Any Order
[a b c d e d e] = apply :append [[a b c] [d e] /dup 2] [a b c d e d e] = apply :append [/dup 2 [a b c] [d e]] [a b c d e d e] = apply :append [[a b c] /dup 2 [d e]] [a b c d d] = apply :append [/dup 2 [a b c] [d e] /part 1] [a b c d d] = apply :append [[a b c] [d e] /part 1 /dup 2]
Any Parameter (Not Just Refinements) Can Be Used By Name
Once a parameter has been supplied by name, it is no longer considered for consuming positionally.
[a b c d e] = apply :append [/series [a b c] /value [d e]] [a b c d e] = apply :append [/value [d e] /series [a b c]] [a b c d e] = apply :append [/series [a b c] [d e]] [a b c d e] = apply :append [/value [d e] [a b c]]
Commas Are Ok So Long As They Are Interstitial
[a b c d e d e] = apply :append [[a b c], [d e], /dup 2] [a b c d e d e] = apply :append [/dup 2, [a b c] [d e]] >> apply :append [/dup, 2 [a b c] [d e]] ** Script Error: end was reached while trying to set /dup
Giving Too Many Arguments Is An Error
>> apply :append [[a b c] [d e] [f g]] ** Script Error: Too many values in processed argument block of APPLY.
Refinements Must Be Followed By A Non-Refinement
>> apply :append [/dup /part 1 [a b c] [d e]] ** Script Error: end was reached while trying to set /dup
But you can pass refinements as arguments to refinements...just use a quote!
>> tester: func [/refine [any-value!]] [refine] >> apply :tester [/refine '/ta-da!] == /ta-da!
No-Arg Refinements Permit LOGIC! But Set NULL or
Remember: the DO FRAME! mechanics do not change anything, besides ~unset~ isotopes being turned to NULLs. So if a refinement doesn't take an argument, the only legal values for that refinement in the frame are # and NULL.
But APPLY isn't DO FRAME!. It's a higher level thing that builds a frame from the values you supply, and then has an opportunity to look over them before running DO FRAME!. So if it sees you gave a #[true] or a #[false] to a refinement with no argument, it will adjust it appropriately.
>> testme: func [/refine] [refine] # = apply :testme [/refine #] null = apply :testme [/refine null] # = apply :testme [/refine true] null = apply :testme [/refine false]
^META Arguments Are Also Accounted For
For those following the profound design points, the DO FRAME! mechanic does not allow you to have isotopes in frame slots. The way you get isotopes through to a function is through meta parameters, and by convention those parameters are quoted or otherwise "leveled up" into non-isotope status.
But as another convenience, APPLY detects when a parameter is meta and will level it up...because the low-level frame mechanics aren't allowed to editorialize:
>> non-detector: func [arg] [arg] ; not a meta argument, isotopes illegal >> apply :non-detector [~baddie~] ** Script Error: non-detector needs arg as ^META for ~baddie~ isotope >> detector: func [^arg] [arg] >> apply :detector [~baddie~] == ~baddie~
I know not everyone has gotten their heads around isotopes yet, but they are critical... this stuff was the missing link to making it all gel.
What's Next?! Making It Easier To Use!
Imagine if we let
<- be an infix operator...taking the name of the function to apply on the left, and a block on the right:
<-: enfix func [ 'action [word! tuple! path! group!] args [block] ][ apply (if group? action [do action] else [get action]) args ]
It's rather slick!
>> append <- [[a b c] <d> /dup 2] == [a b c <d> <d>] >> append/only <- [[a b c] [e f] /dup 2] == [a b c [e f] [e f]]
Of course, you can mix it up with your own freaky ideas, even variadic ones!
>> $: enfixed func ['name [word!] 'args [<variadic> <end> any-value!]] [ args: make block! args apply :(get name) args ] >> data: [a b c] >> (append $ /dup 2, data [d e]) >> print ["data is" mold data] data is [a b c d e d e]
The choice is up to you. Which is what all this is about!