The Grand Leading-Slash "Safety, or Burden?" Question

Overall, I have been tremendously happy with how the ideas of the Big Alien Proposal have worked out.

That started from the concept that when slashes appear, they either come before a function they run, or after a function they suppress execution for.

foo.bar
; ^-- foo is an entity from which BAR is being selected.  BAR is not
; allowed to be an antiform frame, so this syntax cannot invoke a
; function call (though it can invoke an 'accessor', e.g. a "getter"
; which is 0-arity).

foo/bar
; ^-- foo is an entity from which BAR (an a FRAME! or antiform FRAME!)
; is being selected and then invoked.  This will generate an error if
; bar is not a frame or antiform frame.

foo.bar/
; ^-- bar is a field which is an antiform FRAME!, whose execution is
; being suppressed.  This expression will return an antiform frame, or
; an error if not an antiform frame.

foo
; ^-- conventional WORD! reference, will run an antiform frame as an
; action invocation or fetch other values as-is

/foo
; ^-- invocation reference, will run an antiform frame (or plain frame)
; as an action invocation and give errors on other types

foo/
; ^-- action suppression, will give you back an antiform frame as-is
; and error on other types.

(If you're curious about why /foo will run plain FRAME! as well as antiform, while foo/ will not return an antiform frame for plain FRAME!, this is based on the idea that it's better to be conservative when fetching values so that you won't get surprised by getting a plain frame back from foo/~ which gives everything back as-is.)


I've written elsewhere how pleased I am that the way you suppress a function's execution is by throwing up a "barrier" with a separating slash that makes it clear arguments are not being gathered at the callsite. That's really slick.

For this idea to work, something else had to be used for refinements. That meant invention of the CHAIN! datatype has opened a lot of interesting doors, and I find it's quite learnable to see things like trim:auto:tail instead of trim/auto/tail.

I actually prefer it! What some might think of a disadvantage of being "less noticeable" turns into an advantage... trim:auto really could have been a function called trim-auto just as easily. Why would you want a slash to make the fact that it has a refinement "pop"? The slashes to make function calls or suppression pop are much better applied.

So that's all good. :smile_cat: No regrets!

But... Leading-Slash For Functions Rule Hasn't 100% Gel'd

Another part of the proposal was that in order to get tighter control on what was a function or not, you would be required to assign functions using a leading-slash kind of SET-WORD!.

>> foo: func [a b] [return a + b]
** Error: FOO: can't be used to assign antiform FRAME!, use /FOO:

>> /foo: func [a b] [return a + b]
== ~#[frame! "foo" [a b]]]~  ; anti

It hasn't fully settled with me after working with it for some time.

As I mentioned above, colons for refinements was easy to adapt to...and now that I'm adapted, I prefer it.

But I'm still typing test: cascade [add/ even?/]. I love the trailing slashes (and this will be even better when the whole cascade can be done with just even?/add/). But I'm kind of cursing under my breath the thought of having typed test: and having to backspace over it so it says /test:. And then I go "hrmph."

When I'm reading code, I probably appreciate it more than I find it to be "messy". It gives you a better compass. The eye can scan and comprehend much better... it's of particular value when you're not using an obvious function generator like FUNC, but something else. This cues readers to go "oh, I guess that's a function generator".

Yet still... it's a burden in a way the other changes are not. It's the only change that increases the character count.

What's At Stake By Not Enforcing This?

Ren-C has a powerful story about how antiforms can't be put in blocks, which means you can write this kind of code and it "just works":

block2: collect [
    for-each 'item block1 [keep item]
]

assert [equal? block1 block2]

When you compare it to Rebol2/R3-Alpha/Red, it's one of those vastly superior situations. You aren't getting tricked into receiving an ITEM in the FOR-EACH that would generate an unset variable error, or conflate with the state that gets returned when an item can't be picked from a block, or accidentally run a function. It's a solid solution.

But that's only for blocks. What about other places, like objects?

If we don't put barriers on how action antiforms get assigned to variables, we get the problem all over again:

for-each [key value] obj [
    if integer? value [  ; oops, what if VALUE is an action antiform!
        print "Found an integer"
    ]
]

There's no way in this case to say "variables can't hold antiforms". Logic is an antiform. Words holding antiform frames are actions.

Getting this under control with slashes is the kind of thing I've been trying to do for a long time, I've just never had the syntax. Leading slashes felt like it could be the key:

for-each [key value] obj [...]  ; value can't be frame antiform

for-each [key /value] obj [...]  ; value must be frame antiform

for-each [key ~/value] obj [...]  ; value may be frame antiform

But if these rules are applied everywhere, what you have to do gets more complex:

set $x does [print "Is this an error?"]

set $/x does [print "Do you have to do this?"]

>> var: $x
== x  ; bound

set var does [print "If this errors, how to make VAR into bound /x?"]

set:active var does [print "Do you use refinements?"] (or just SET:ANY ?)

Nothing is free. And the already more complicated world where x: is a CHAIN! instead of a fundamental different type of word has its own issues, that these all pile on top of.

There's Likely Not Enough Value In Optional Slash

If /foo: func [...] [...] will enforce that the thing you're assigning is an antiform action, but foo: func [...] [...] still works... I have a feeling that the complexity it takes to offer the feature doesn't give a sufficient payoff to be worth it.

You have everyone paying the tax of dealing with complicated path structures and bookkeeping--vs. being able to just SET and GET words and tuples at will... and then you're not even giving any additional guarantees in the source.

This makes me feel like it really is an all-in or not-at-all situation.

Long Story Short: I'm Still Weighing It

I'm not ready to make a verdict.

The techniques for working with these new CHAIN! and PATH! situations are still being learned. Most of my hesitance isn't from the looks or typing an extra character, but from frustrations in that...and maybe that frustration will lessen as I work on it more.

2 Likes