Intentionally Specializing With TRASH

When you make a new FRAME! for a function, it has all of its fields unset (e.g. set to "trash", the ~ isotope)

 foo: function [x [integer!] /y [integer!] /z] [...]

 >> f: make frame! :foo
 == make frame! [
      x: ~
      y: ~
      z: ~
 ]

If you were to try and do f on that frame without setting x to something, you would get an error. You would also get an error if you said f.x: "not an integer" and tried do f.

You can just leave y and z alone. If you DO F it will just mean they're taken as unused, and will be received by FOO as nulls.

But what if you want to actually SPECIALIZE a function with TRASH, e.g.:

unset: specialize :set [value: ~]

Custom Escape Value?

A first thought I had was we could do something along the lines of just telling SPECIALIZE a value you want to use as the escape:

 foo-no-y: specialize/trash :foo [y: <trash-escape>] <trash-escape>

It sounds plausible, until you think about scenarios where some of your data is being passed in from outside:

 make-specialfoo: function [data-from-elsewhere] [
     return specialize :foo [
         x: data-from-elsewhere
         y: <trash-escape>
     ]
 ]

What if the passed in data-from-elsewhere just coincidentally used the same magic escape value you did? :frowning:

It's always the case in the beginning that a weird escape value seems like it "wouldn't happen". But it becomes a self-fulfilling-anti-prophecy, in the sense that once you give the weird value meaning that people start using it, and it's no longer the rare occurrence it was on day one.

I'm not saying such a construct shouldn't or couldn't exist and be useful somewhere. But it's a pretty big weakness for "the" SPECIALIZE to have.

Assume only user-assigned trash are specialized

The current implementation of SPECIALIZE doesn't keep track of whether you assigned a field or not.
It just starts with frame fields as trash, runs your code bound into the frame, and when the code is done it assumes anything that's still trash is unspecialized. So these act the same:

 specialize 'foo [x: 10 y: ~]
 specialize 'foo [x: 10]

Technically speaking, it's actually possible for the system to write a bit on the value cell in the frame... and notice whether that bit is still set at the end. So it could tell a "stale" trash from a user-written trash.

Leveraging such internals makes me a bit uncomfortable, in the sense of being something the user can't do. I'd like to see basic mechanics exported so that users could write their own variations of SPECIALIZE.

Make removing args a separate refinement

In the lazy/obvious department, just have a list of the arguments you want to remove, passed as a block or something:

specialize/remove 'foo [x: 10] [y]

Not very imaginative.

Expose A New Meta Bit

A more imaginative concept might actually let the user programmatically request the field be removed from the frame's interface. It might even reuse the mechanics for PROTECT/HIDE:

specialize 'foo [
    x: 10
    protect/hide 'y:
]

A backwards-quoting operator might even do that with a nicer syntax...perhaps a function that is bound to work in concert with SPECIALIZE (the way KEEP works with COLLECT):

specialize 'foo [
    x: 10
    y: hidden  ; or SPECIALIZED-OUT/etc...it would quote left and fiddle bit
]

It may be able to just build on the PROTECT/HIDE feature, and if so it might bring more justification to its existence. Or maybe just the plain old PROTECT bit...if you're saying the field is protected, it's trash and can't be changed, what else could you mean by that than "it's in its final form, so remove it from the interface"?

The idea is that I want to be able to flip back and forth between MAKE ACTION! from a FRAME! and MAKE FRAME! from an ACTION!. This is the long game of writing your own specialization operators. It can't be done completely in-band with values and trash...since they represent valid states for a function's arguments. So as long as the function call itself can't read this hidden bit controlling frame creation and derive meaning from it, it should work.

The simple answer here is simply that functions can't take TRASH as a normal parameter.

Even though it's a "stable" isotope, you have to make a parameter ^META to receive it.

This means you'd write:

unset: specialize :set [value: '~]  ; e.g. value is ~

The downside of this is that any value put in the frame for SET would have be META.

set-to-5: specialize :set [value: meta 5]  ; e.g. value is '5

The alternative would be to disallow SET to a trash value.

A function like APPLY can do this automatically. It's in control of the assignments so it can look at the parameter it's assigning and do the right thing with the product of the evaluation it did.

But the code inside of the block passed to SPECIALIZE is freeform and it's not in control of the assignment statements. So by the time your code is done running, it's too late to know if value: ~ meant you didn't assign it or you assigned it deliberately.

I've tried to minimize the number of places that ^META parameters show up. But when they show up, anything that manipulates frames directly has to know about that to write correct code.