Critiquing Red's Updated APPLY Implementation

Ren-C added a super-duper APPLY in August 2021:

About two years later, I observe Red has published their own implementation of APPLY...with some seeming similarities:

Red Programming Language: Dynamic Refinements and Function Application

But there are significant mechanical and usability differences.

Red inherits complexity from the fact that they didn't merge refinements and their arguments together. This keeps them tied to problems that arise from ordering, and keeping the enablement of a refinement in sync with its value(s). It's clear to me that "multi-arg-refinements" has proven to be not worth it in the design vs. having a single unified nullable value for each named argument...and this is only one of the many places that bear that out.

They do offer an idea how to create something like a FRAME!... but it's a higher-level usermode concept instead of the lower-level basis for APPLY-like abstractions. The post gives an implementation called make-apply-obj-proto:

red>> o-fctm: make-apply-obj-proto/with 'find/case/tail/match [series: [a b c] value: 'a]
== make object! [
    series: [a b c]
    value: 'a
    part: false
    length: none
    only: false
    case: true
    same: fal...

red>> apply-object :find o-fctm
== [b c]

Ren-C bakes this idea in as the core of function application. You can build a FRAME! for the function (note the tail of the match is a multi-return...you can get both the begin and end in a single call...so there's no /TAIL here):

>> f: make frame! :find/case/match
== make frame! [
    series: ~
    pattern: ~
    part: ~
    skip: ~
    reverse: ~
    last: ~
]

Notice /CASE and /MATCH didn't take arguments, so they have been specialized out of the frame as they were already mentioned as being in use.

Then you can fill in the frame with required (and optional) values:

>> f.series: [a b c]
== [a b c]

>> f.pattern: 'a
== a

And it remembers what function it is for, so you can EVAL it without getting it mixed up. (As a nice bonus in our definition of FIND here, the multi-return gives you the head and the tail if you want it):

>> [begin end]: eval f
== [a b c]

>> begin
== [a b c]

>> end
== [b c]

They say of make-apply-obj-proto: "But you may see that this is verbose and inefficient, making a whole object just for a call like this. And you'd be right. It's just an example. You don't want to recreate objects like this, especially in a loop. But you don't have to. You can reuse the object and just change the important values in it."

This is the opposite philosophy to Ren-C. These frames are the foundation of function invocation...and so they are always built. The keylist of the object lives with the function definition and is pointed to by the frame, and so each instance only takes up the cells of the arguments.

(Note: If you wanted something directly comparable to make-apply-obj-proto it really could just be an option passed to APPLY which gave back the frame it builds without calling it.)

Anyhow... I'm glad they've undertaken this... because it brings us closer to assembling comparable examples. It lays bare the fact that when I undertake "complexity" it is because failure to design the system to handle relevant cases pushes that complexity onto the user...

2 Likes