So far, /leading
/slash
/notation
has just been evaluator inert, lining up with Rebol2 and Red and R3-Alpha:
redbol>> /foo
== /foo
That inertness doesn't seem to get leveraged much. And in Ren-C it's a particularly weak choice, since the evaluator has generic quoting to get you the literal result:
ren-c>> '/foo
== /foo
Also, leading slash is an actual PATH!...encompassing arbitrary patterns like /lib.append/dup/part
. So I've always been wondering if there was some interesting evaluator behavior for it, like...
-
...asking to pick from "global scope": if your function has an argument called ALL then /ALL might get you the definition outside your function? (Something like
::foo
in C++) -
...maybe a shorthand for
self/foo
for picking members out of objects inside of methods?
Yet nothing has ever really stuck. But @IngoHohmann pointed out that there's a basic thing that leading slashes might do for us which may have been overlooked...
...simply running functions.
"But WORD!s Run Functions, Why Should /FOO Do That?"
There are many reasons, but the biggest one is...
I feel pretty much 100% certain it is time that we switched to a world where not all WORD!s holding ACTION!s will run them.
It's too cumbersome when writing generic code to worry that a value you got "from somewhere" and put into a SET-WORD! has to be handled with special operators:
>> var: select obj 'item
>> if integer? var [print "INT"]
Muhaha the next thing at your callsite was [print "INT"] ; eek, VAR was action
>> var: first block
>> if integer? var [print "INT"]
HAH! Did you think blocks were safe? Not at all: [print "INT"] ; in blocks too!
Whether you think of it in terms of "security"--or simply bugs and chaos--this persistent tax on Redbol code authors has lacked a palatable solution. Putting a GET-XXX! on every access is ugly, and easy to forget. What we've ended up with is a mishmash...where people are constantly forced to choose between deciding if the brokenness is likely enough to cause a problem that it's worth it to make the code ugly.
(When code ages, it's like it develops some sort of pox--as leading colons are added on an ad-hoc basis, then no one really knows if they're safe to remove.)
I propose that only specially marked assignments would automatically run a function through a word reference, requiring UNRUN or ^META access to get the action value literally.
>> /foo: func [x] [print ["X is" x]]
== ~#[frame! [x]]~ ; anti
>> foo 10
X is 10
>> ^foo
== ~#[frame! {foo} [x]]~
>> unrun foo
== #[frame! [x]]
If a function is assigned through a plain SET-WORD!, then that would be inert by default...but able to take advantage of this new leading-slash execution.
>> foo: func [x] [print ["X is" x]]
>> foo
== #[frame! {foo} [x]]
>> /foo 10
X is 10
Compliance Isn't Actually That Ugly!
If you look at the definition of an object, then annotating the member functions isn't really so bad:
obj: make object! [
data: 1
/accessor: does [return data + 1]
]
This also gives us some extra ammo: it could motivate using OBJ/ACCESSOR to invoke the function, and have OBJ.ACCESSOR give it back as-is.
You could then tell where the function invocations were: XXX.YYY.ZZZ is never a function call. XXX/YYY/ZZZ always is, and you know YYY is a function. XXX.YYY/ZZZ is a function call, and you know ZZZ is a function. Readability is improved by a lot!
It Can Be A Nice Dialecting Pattern
It's useful in dialects where plain WORD! references are taken for another meaning. For instance, UPARSE by default assumes a word means a combinator, so if you want to run a function that uses parse rules to gather its arguments you need something else:
>> parse [1] [/negate integer!]
== -1
>> parse [1 2] [/add integer! integer!]
== 3
Initially I tried this with terminal slashes, as negate/
and add/
, but that doesn't look as good (and separates the functions from their arguments).
What Do We Lose?
Because I was trying to think of a meaningful evaluator behavior for leading-slash values, I didn't do much with them. But eventually I decided to use them in New Apply:
They're nice because they break up the space:
>> apply :append [[a b c] [d e] /dup 2]
== [a b c [d e] [d e]]
But SET-WORD! is reasonable at this, and commas can make it more visually separate if needed:
>> apply :append [[a b c] [d e] dup: 2]
== [a b c [d e] [d e]]
>> apply :append [[a b c] [d e], dup: 2]
== [a b c [d e] [d e]]
Of course whatever we put here is being overloaded. If you want a SET-WORD! for assignment purposes, you'd have to put it in a group:
>> apply :append [(var: [a b c]) [d e], dup: 2]
== [a b c [d e] [d e]]
>> var
== [a b c]
So we could think of this similarly. If you wanted to use a refinement-style path here, you just do it in a group:
>> apply :append [(/reverse [a b c]) [d e], /dup 2]
== [c b a [d e] [d e]]
APPLY is a dialect, and there are always going to be some tradeoffs made. There's only so many parts.
It's probably best to leave APPLY as it is. I don't think we're going to be in the midst of some epidemic where suddenly every function invocation is done through a leading slash and it's going to be contentious. There will also be ways of running a function through REEVAL or maybe a dedicated RUN function that won't use the slash...
So nothing needs to be lost, really.
The Big Win is that the Obvious Code is the Correct Code
I've done some tentative implementation on all this, and all together, it seems pretty solid
Really all you're doing is paying the cost of an extra (easy-to-type) character to say that a word is intended to execute a function without needing to explicitly be told to.
There will be ways to subvert it, as of course you could do this:
>> /func: enfix lambda [left [set-word!] spec body] [
do compose [/(as word! left): lib/func (spec) (body)]
]
>> cheat: func [] [print "Breakin the law, breakin the law..."]
>> cheat
Breakin the law, breakin the law...
But we wouldn't make you do it that laboriously, if you're making something where words need to be associated with functions that run automatically. And Redbol would do it through some evaluator parameterization as opposed to a mechanism like that.
However, the general expectation would be that most people would embrace the slash, as a useful piece of information...that makes everything work more coherently.