There May (?) Be More Than One Kind of Promotion
Currently there is some code which lets you "build a frame by example":
>> make frame! [match integer!]
== #[frame! [
test: &[integer!]
value: ~
]]
Even though MATCH takes two arguments, the evaluator went as far as it could and then stopped, leaving extra arguments unspecified.
But... what about refinements that take arguments?
Historically what you would get with refinement promotion would move the DUP to the head of the parameter list and make it not a refinement... leaving :PART and :LINE as refinements:
>> make frame! [append:dup [a b c] [d e]]
== #[frame! [
series: [a b c]
value: [d e]
dup: ~ ; now has an associated PARAMETER! that's not a refinement
part: ~ ; still a refinement
line: ~ ; still a refinement
]]
But if you're just looking at this as a frame without comparing the parameter list, it doesn't seem different from how it would look if you hadn't specified the :DUP at all.
In order to not lose information about this specific instantiation, we need what might be called "narrowing refinement promotion":
>> make frame! [append:dup [a b c] [d e]]
== #[frame! [
series: [a b c]
value: [d e]
dup: ~
]]
This means any refinements you didn't specify are gone from the interface. Because you're trying to encode the intent of an invocation.
"The user has spoken: They want APPEND:DUP"
What If This Was The Only Refinement Promotion Type?
From the earliest days of FRAME!, I've been assuming that this is what should happen when you MAKE FRAME! on APPEND:
>> make frame! append/
== #[frame! [
series: ~
value: ~
part: ~
dup: ~
line: ~
]]
But in an parallel universe, what if I had assumed instead:
>> make frame! append/
== #[frame! [
series: ~
value: ~
]]
>> make frame! append:part:dup/
== #[frame! [
series: ~
value: ~
part: ~
dup: ~
]]
>> make frame! append:dup:part/
== #[frame! [
series: ~
value: ~
dup: ~
part: ~
]]
The Latter Is The Only Use for Refinement Promotion (So Far)
e.g. I've never actually wanted to make a refinement that takes an argument become non-optional, unless the intent was specifically to model an invocation (that wouldn't be using any other optional parameters).
I do know I still want to be able to get a copy of a frame I can fill in all the fields as I wish. But if I'm doing that, I'm not passing a refinement chain. I either fix parameters completely...or not at all.
Anyway, MAKE FRAME! should be consistent in terms of how it answers. If it's narrowing when given a variadic example (which it must be), seems it should be narrowing when given a refinement chain.
MAKE can legally perform evaluations:
>> make frame! [append reverse [a b c]]
== #[frame! [
series: [c b a] ; had to evaluate to get that
value: ~
]]
>> make frame! append/
== #[frame! [ ; should be consistently narrowing
series: ~
value: ~
]]
COPY FRAME! doesn't set PARAMETER! fields to ~ antiforms... and it shouldn't. And TO FRAME! of a FRAME! has to be a synonym for COPY.
So... uh, name that operator:
>> XXX append/
== #[frame! [
series: ~
value: ~
part: ~
dup: ~
line: ~
]]
This has been a bit of an eye-opener.
I'm going to have to think about it.