Positional APPLY as default, APPLIQUE for FRAME!-style apply


#1

Historical Rebol APPLY had a difficult time with handling refinements. The order in the spec leaked through to the refinements themselves. Hence:

 rebol2>> f: func [x /ref1 y /ref2 z] [print [x ref1 y ref2 z]]
 rebol2>> apply :f [1 2 3 4 5]
 1 true 3 true 5

It was rather sketchy and confusing. Some of the most irritating examples were found in the module code, with examples like:

return map-each [mod ver sum name] source [
    apply :load-module [
        mod true? ver ver true? sum sum no-share no-lib import true? name name delay
    ]
]

Ren-C started aiming at the concept of introducing FRAME!s, and came up with the idea of where you'd use a BLOCK! that acted like a spec block for an object, whose keys were the arguments to the frame. Using techniques similar to SPECIALIZE it would automatically intuit when a refinement was active, based on making any of those arguments non-null:

 ren-c-old>> f: func [x /ref1 y /ref2 z] [print [x ref1 try :y ref2 try :z]]
 ren-c-old>> apply 'f [x: 1 y: 3 z: 5]
 1 /ref1 3 /ref2 5

 ren-c-old>> f: func [x /ref1 y /ref2 z] [print [x ref1 try :y ref2 try :z]]
 ren-c-old>> apply 'f [x: 1 z: 5]
 1 _ _ /ref2 5

Thanks to NULL's special status as a non-value, you could even have a way to use a parameter to opt out of a refinement despite its specification:

 ren-c-old>> f: func [x /ref1 y /ref2 z] [print [x ref1 try :y ref2 try :z]]
 ren-c-old>> apply 'f [x: 1 y: null z: 5]
 1 _ _ /ref2 5

Then, there was a usermode implementation of the old style of APPLY that was built on top of this notion of building frames and executing them. That was a valuable test of letting people build APPLY-like things.

But positional APPLY is useful, and deserves the default

One of the downsides of using the frame-making style of apply is having to know the names of the arguments. That's not always going to be desirable. If you write a sort function and someone is asked to pass you a comparator, why should you need to know the names of the arguments?

In the Beta/One mindset of "be compatible with history unless you have a 100% win reason for changing things", APPLY gets the name. But it won't work the same way for refinements. If you want a refinement, you'll have to refine the function, e.g.:

 >> f: func [x /ref1 y /ref2 z] [print [x ref1 try :y ref2 try :z]]
 >> apply 'f/x/y [1 3 5]
 1 /ref1 3 /ref2 5

 ren-c-old>> apply 'f/x/y [1 null 5]
 1 _ _ /ref2 5

So you will never see slots in the block that correspond to the refinements themselves. Yet you can still opt out of a refinement via the magic of NULL. Best of both worlds.

For now, the Ren-C FRAME-based version has odd name: APPLIQUE

I feel like there's a missing piece in here somewhere, that you can't say something like do make frame! :f [....]. But that would require MAKE FRAME! taking two arguments, the function to make a frame for and the block to run in. Also, it would be less efficient than making a native that combined the frame making and the execution.

So I'm picking a wacky name for it that means something-like-APPLY. I don't think the name is sought after for other purposes, so if it changes, it will be easy to bridge compatibility. But it still means "apply" and it's one word. It isn't the end of the world if it sticks around, I think it's kind of amusing. :slight_smile:

In any case, if you'd been using the frame-style APPLY then you should switch over those calls to APPLIQUE. An error message on APPLY will be raised for a little while, until it settles in on the positional definition...which should be compatible as-is with any old APPLY calls that didn't use refinements.