When you make a new FRAME! for a function, it has all of its fields unset (e.g. set to "none" ~)
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 NONE, 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/none :foo [y: <noneescape>] <noneescape>
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: <noneescape>
]
]
What if the passed in data-from-elsewhere
just coincidentally used the same magic escape value you did? 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 nones 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 none, runs your code bound into the frame, and when the code is done it assumes anything that's still none 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" null from a user-written null.
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.
Likely Winner: 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 NULL 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 nulls...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.