APPLY II: The Revenge!

...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.

:dizzy: :dizzy: :dizzy:

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!

2 Likes

A post was merged into an existing topic: Design Issues for New APPLY

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:

I think it's worth trying.

I've been giving it a shot, and trying out other ideas too.

I tried colon, which looks pretty good in isolation:

>> 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]]

But it kind of gets lost in the noise of all the other usages of colon:

thing: append : [series :value]

For most anything you'd put in that position, it struggles with the question of "why would it mean APPLY". Even if & wasn't hideous (and it is), there's no real tie-in that it has to other uses of & we might imagine...so having it do function application just looks random:

thing: append & [series :value]

What <- has going for it is that -> is the lambda function generator. So it looks like it's in the family of "function things". Pointing right creates a function, pointing left applies it.

>> (x -> [print [x + 20]]) <- [1000]
== 1020

So I think it's basically the best option. But...again...configurable for those who want to think differently!

1 Like

So one possibility that's growing on me is //

thing: append // [[a b c] <d> /dup 2]

It's no longer going to be a PATH!, just because //x and x// are paths. (It will be a WORD! that has some inter-convertibility to paths for operations like JOIN, e.g. join 'a/b as path! '// would get you the path a/b//)

It has a couple of things going for it:

  • Slashes are now intimately entwined with function application in the core evaluator, so having a slashy thing be APPLY makes some sense

  • It's easy to type...doesn't require hitting shift, flows very naturally.

  • Visually non-disruptive; it's sort of "shapeless"

It's used for comments in JavaScript and C++, but the syntaxes are 99% incompatible anyway. I've made peace with semicolon as the comment character years ago, so desiring // for comments isn't an issue for me anymore.

If we wanted to bake the behavior into path dispatch, we could glue it onto the APPEND:

thing: append// [[a b c] <d> /dup 2]

But, that makes it more built in to path dispatch (which is undesirable) and I don't think it looks as good.

I'm leaning a little more to liking this than the left arrow, because the arrow is kind of "noisy" as symbols go...and the idea that it's a meaningful "direction of flow" is a bit forced.