I've taken the big bold leap into the world of fully LIFT'ed FRAME!s.
As a refresher: this is the idea that as far as the "API" for calling functions is concerned, you always communicate via lifted values.
That means when you're building a FRAME! to call a function, you don't have to be concerned whether that function specified its argument as ^META or not. Once the invocation of the function happens, it will unlift non-^meta arguments as part of its execution.
Hence if an argument changes from ^META-to-non-meta (or vice-versa), that won't impact callsites that build frames for it.
^META's LIFT-ON-ASSIGN, UNLIFT-ON-FETCH Helps
The new behavior of ^META-WORD! is extended to TUPLE!, that makes this pretty easy to deal with when working with a "raw" FRAME!:
>> f: make frame! append/
>> f.^series: [a b c]
== [a b c]
>> f.series
== '[a b c]
>> f.^value: spread [d e]
== /~(d e)~/ ; antiform (splice!)
>> f.value
== ~(d e)~
>> eval f
== [a b c d e]
That's a little unfortunate-looking... messier... but it's necessary, right? (keep reading...)
Simplifying Dialect For APPLY + SPECIALIZE
While you have to build a FRAME! with values lifted, I didn't want to make higher-level tools like APPLY and SPECIALIZE look ugly. So they lift for you:
>> apply append/ [[a b c] spread [d e] dup: 2]
== [a b c d e d e]
>> sub10: specialize subtract/ [value2: 10]
>> sub10 1030
== 1020
But I've proposed a :FREEFORM variant form of APPLY and SPECIALIZE...in which the code is bound into the frame. In this case, the dialect isn't in control... so you would have to use the lifted forms of assignment. (or would you? keep reading...)
This form is more awkward and can't take advantage of positional assignment:
apply:freeform append/ [^series: [a b c] ^value: spread [d e] ^dup: 2]
But it lets you write arbitrary branching and looping code :
>> apply:freeform append/ [
if 1 = random 2 [
^series: [a b c]
^dup: 2
] else [
^series: [q r s]
^dup: 3
]
^value: spread [d e]
]
== [q r s d e d e d e]
Could The "Ugliness" Be Moved Around, Somehow?
It seems kind of... unfair. How can the dialected form deal with something easily, that the non-dialected situations don't?
In other words: is there some way to simplify the :FREEFORM versions... or things like ADAPT and ENCLOSE... or building "RAW" frames?
My "invasive thought" is this:
- why couldn't
(foo: ...)store a lifted-but-decayed form of the right hand side - and
(^foo: ...)store a lifted-but not decayed version of the right hand side?
This is being driven by the only truly "unlifted" state a frame normally "needs" (hand-waving a bit here, actually
) is unlifted trash to say it's unspecialized. What would be the harm of lifting everything, and having a special operation for setting to the unspecialized state?
It would imply, I think... (?)
-
(foo)unlifts the stored value (executing actions, erroring on TRASH!, etc.) and refuses to unlift unstable antiforms -
(^foo)unlifts with no execution (trash! as-is, action! as-is), and is willing to unlift unstable antiforms (pack!, error!, void! etc. as-is)- I have another slightly invasive thought that ^(foo) might permit "unsurprising" voids or actions, but that's for another thread.
This is a slippery invasive thought. It has to be wrong, doesn't it? Too good to be true? ![]()
But what if it isn't? ![]()
It's not changing the usual chain of evaluation. If you say (1 + 2 comment "hi") that's not lifted... it's a plain old 3 and a "plain old" void!.
So this means when you say (foo: ~) you would be setting FOO to an "ordinary" trash value (a lifted one, in stored representation). This would cause errors if fetched via (foo) which would unlift and treat it as active. But if you said (^foo) you would get back the trash state as-is.
This means there would be a state more trash than trash... unspecialization. A variable holding a non-lifted trash, which would trip up even fetches with ^foo. And this goes along with my concept of ACCESSOR functions... moving them from something "hidden" to something that's actually exposed... something beneath the layers that (foo: ...) or (^foo: ...) alone can assign.
So you'd need tools to go beneath assignment, but these tools have already been theorized (I've called it "TWEAK")
More investigation on this needed, but...
...it's actually just an "all-in, fully-exposed" version of the more "behind-the-scenes" idea I was already implementing, where objects/modules/lets/etc. were storing lifted values...and having special exceptions in the unlifted range.
The consequence is we can limit (f.^foo: ...) or (^bar: ...) cases to those truly concerned with unstable isotopes. Which if that can be accomplished, seems to mean only those who need it pay for it.

