After debating the topic for some time, I decided FENCE! should indeed be evaluative.
But rather than setting what it does in stone... let's imagine that what FENCE! does when it evaluates is actually to pass the fence to a function you can redefine in the environment.
demo: func [] [
let fence!-EVAL: func [fence] [
print "I got a fence of length" length of fence
return try second fence
]
return {a b c}
]
>> fence!-EVAL: identity/ ; inside DEMO has a LET of its own choice
>> demo
I got a fence of length 3
== b
>> {x: 10 y: 20}
== {x: 10 y: 20} ; ...!
By allowing FENCE! to be evaluative, you can even allow it to be unevaluative if you choose, in a context:
![]()
That's an idea so powerful...that having block!-EVAL, group!-EVAL, etc. functions all being looked up and run seems like it needs to be done right now. Whatever optimizations are needed to make it not slow in the general non-overridden case can be attended to.
I've been working out the definition of CONSTRUCT as FENCE!'s default behavior, a dialect specific to the efficient and essential creation of objects.
But since FENCE! doesn't have any usages in the wild right now, it's a perfect guinea pig for the technique. So let's give it a shot, because this is what the whole thing is supposed to be about. ![]()
20 Minutes Later...
It works. ![]()
>> {x: 10 y: 20}
== #[object! [
x: 10
y: 20
]]
>> fence!-EVAL: func [f] [print ["Length is" length of f]]
>> {x: 10 y: 20}
Length is 4
>> fence!-EVAL: construct/
>> {x: 10 y: 20}
== #[object! [
x: 10
y: 20
]]
Years to ponder, minutes to implement.
(Of course, it hinges on the blood sweat and tears of pure virtual binding.)
Despite being extensible... IT'S FASTER THAN MAKE OBJECT! because instead of looking up MAKE and looking up OBJECT! and building a frame and all that, it calls a native arity-1 intrinsically, with no frame at all! So you're paying for fewer word lookups and not even making a frame the resulting function (if you are using an intrinsic, which CONSTRUCT is, and presumably whatever other default maker would be too). ![]()
But crucially here...you should always have fallbacks for doing the creation without needing to use the lexical form. So you can use FENCE! creatively, however you like... but still have MAKE OBJECT! (or whatever) passed blocks to get the behavior if you need it.
And if there's only hookpoints for BLOCK!, FENCE! and GROUP!... but not their variations.. you'll always have quoted '[blocks] and $(groups) etc to fall back on if necessary.
Wild Example #1 : Progressive Parsing
Let's say you wanted to do a parse, but not all at once... rather continuing it a little piece at a time with handling code.
data: [
The Sharp Gray @Fork "Quantum Leaped" Over The Lazy @Red
]
f: lambda [rule [block!]] [
parse data [accept [rule, elide data: <here>]]
]
The short (meaningless) name gets things about as brief as you can get in "historical" code:
designer: f [some word!, one]
assert [designer = @Fork]
occurrence: f [text!, elide 'Over]
assert [occurrence = "Quantum Leaped"]
other: f [collect [
keep one, keep ('Intellectually) keep spread across to <end>
]]
assert [other = [The Intellectually Lazy @Red]]
But what if you wanted to do it so that a FENCE! was an implicit call to the parse steps?
fence!-EVAL: lambda [rule [fence!]] [
rule: as block! rule
parse data [accept [rule, elide data: <here>]]
]
Then your calls could look like this:
designer: {some word!, one}
assert [designer = @Fork]
occurrence: {text!, elide 'Over}
assert [occurrence = "Quantum Leaped"]
other: {collect [
keep one, keep ('Intellectually) keep spread across to <end>
]}
assert [other = [The Intellectually Lazy @Red]]
Yes, it works!
![]()
Is It THAT Different Than Using A Function? YES.
The difference is significant, and more than just cosmetic.
We've seen arguments against "more parts" before... e.g. saying that GROUP! is not necessary if you have BLOCK! (or vice-versa) because you can always split your intent up into multiple tokens. But it's very Turing Tar-Pit to say "oh it's all the same"... because it is not the same. ![]()
When the FENCE! can encode itself in one value vs. needing a word-and-a-value, you get better compositional properties. You have smoother meta-analysis when building higher level things that the fences are composed into (vs. WORD!+BLOCK!)
I see mountains of potential here.
![]()