Big Alien Proposal 🛸 "/WORD" Runs Functions


After about two years of learning to work with antiform-based actions, they are reasonably understood (and understood as the antiform of FRAME!)

The idea of FUNC returning plain FRAME! and then having a refinement-style assignment promote (demote?) it to an antiform is bad.

>> foo: func [msg] [print ["This" name]]
== #[frame! [msg]]

>> foo
== #[frame! [msg]]

>> run foo "Sucks"
This Sucks

>> /foo: func [msg] [print ["This" msg]]
== ~#[frame! [msg]]~  ; anti

>> foo "Sucks As Well"
This Sucks As Well

It is way too easy to forget the slash.

But I've Been Up All Night Smoking Crack. So Hear Me Out...

What if FUNC returned an antiform FRAME! and you had to use the slash in order to say "yes, I meant to assign that?

>> func [msg] [print ["This" msg]]
== ~#[frame! [msg]]~  ; anti

>> foo: func [msg] [print ["This" msg]]
** Error: Cannot save antiform frame with foo: (use /foo:)

>> /foo: func [msg] [print ["This" msg]]
== ~#[frame! [msg]]~  ; anti

>> foo "Might Have Potential"
This Might Have Potential

/FOO: and /FOO could mean "antiform frames are ok to be assigned here.

  • You can use it in function specs, e.g. func [a] [...] would not take an antiform action, but func [/a] [...] would.

  • You could use it in enumerations, e.g. for-each [key /value] obj [...] would allow you to receive frame antiforms, but for-each [key value] obj [...] would not.

Crazy Talk :crazy_face: What If Refinements Were CHAIN! ?

Under the rule above, you'd have to make your methods in your objects look like this

obj: make object! [
    data: 1
    /accessor: does [return data + 1]
]

Let's say that in order to call ACCESSOR you had to use a PATH!, e.g. OBJ/ACCESSOR. And then...for refinements...we use CHAIN! ... our new interstitial peer to TUPLE! and PATH! that uses colons.

This would make :word the new refinement (blank-headed chain):

obj: make object! [
    data: 1
    /accessor: func [arg [integer!] :refine1 [integer!] :refine2] [
        if refine1 [
            return data + arg + either refine2 [negate refine1] [refine1]
        ]
        return either refine2 [data + negate arg] [data + arg]
    ]
]

And if you want to use those refinements:

obj/accessor:refine1:refine2 10 20
  • By looking at it, you can tell that's a call to a function... it's only the weird exception of WORD! that were assigned with /word: where you look at word and can't tell in advance

    • compare with Redbol foo/baz/bar/mumble - You have no idea what's going on.

    • Ren-C made an improvement with foo.baz/bar/mumble where you know due to the use of refinements that foo.baz must be a function, but you're still out of luck if you just see foo.baz

    • This gives us foo/baz:bar:mumble, where we get a more intuitive sense of where the function is (it's /baz, the thing after the slash) and in the diminished case we'd still know foo/baz was a function call

  • Field selection via dot of actions could always not run the action even if it's an antiform, e.g. obj.accessor just gives you the frame antiform as is

    • The impact of these casually extracted antiforms would be limited to places that were rigged to accept the antiform frames

(append:dup [a b c] [d e] 2) is different, but is it bad?

It shifts :foo to be unrelated to "getting"...it would be inert, and used in things like APPLY.

And with slashes being so applicable to function application, it seems inevitable that // be the operator (though you could use :: if you want, these will be WORD!s now)

append // [[a b c] [d e] :dup 2]

:dup would become REFINEMENT?. This would mean that leading colon is how you imply optionality, not slash.

>> [a b c]: pack [1 + 2 10 + 20]
** Error: not enough values in antiform pack for assignment

>> [a b :c]: pack [1 + 2 10 + 20]
== 3

>> reduce [a b]
== [3 30]

In these places, if you used a slash you'd be saying you'd accept action antiforms. You can use both a slash and optionality: /:c. And you can circle it too, @/:c, if you really want to !

Execution Suppression

Accomplishing execution suppression could be done with a terminal dot on a WORD!, or the usage of any TUPLE!. I always thought foo. as "I mean FOO. Period." had potential.

>> append [a b c] [d e]
== [a b c] [d e]

>> append.
== ~#[frame! [series value :dup :part :line]]~  ; anti

But now we'd have it so foo.bar would be enough to get the execution suppression without needing to say foo.bar. because even antiform frames won't run unless you say foo/bar :bangbang:

And there'd be far fewer occasions where you'd need to use foo. "just to be safe". Because you'd have a sense from wherever it was assigned if you didn't have to worry about it.

You wind up with a different look:

>> apply :append [[a b c] [d e] /dup 2]

vs.

>> apply append. [[a b c] [d e] :dup 2]

I can see how the period might suggest (apply append) [[a b c] [d e] :dup 2] if you didn't know what it meant. But if you don't know what things mean, then you can interpret things all kinds of ways.

It doesn't seem all that measurably worse--if it's worse at all. Looking past this one particular case, the benefits are enormous. And you can always say apply get $append [[a b c] [d e] :dup 2] if you want to be verbose about it. (This particular case of course has append // [...])

And We Preserve the /FOO to Run A Function or Frame

>> tester: make frame! append.
== #[frame! [series value :dup :part :line]]

>> tester.series: [a b c]
>> tester.dup: 2

>> /tester [d e]
== [a b c [d e] [d e]]

If it's already an action antiform, then the slash can just be commentary for the reader to say "if not obvious from the context, this is a function call"

:exploding_head:


I only just came up with this, but it's based on understanding the pros and cons of prior attempts, and the benefits are easy to articulate.

I am especially fond of being able to look at XXX/YYY and know that's going to run a function, and that XXX.YYY never will.

This critically narrows the scope of negative impacts of antiform FRAME! by making it only relevant to dispatch from WORD!

...and of course the security of consciously declaring when you're assigning something to a variable that can run arbitrary code from a mere word reference is very important.


By being forced to say foo.bar or foo/bar you can't have a situation of "run it if it's a zero arity function or just give me the value". But maybe that's what foo/bar/ could mean if it ever comes up.

2 Likes