Does SET/ANY, GET/ANY-thing matter anymore?

Historical Rebol uses SET/ANY and GET/ANY to allow you to read and write unset variables:

rebol2>> unset 'foo

rebol2>> get 'foo
** Script Error: foo has no value

rebol2>> unset? get/any 'foo
== true

rebol2>> set 'bar #[unset!]
** Script Error: word needs a value

rebol2>> unset? set/any 'bar #[unset!]
== true

(Note that Ren-C calls the unset variable state "TRASH".)

Calling a Refinement /ANY May Not Be A Good Precedent

ANY is a very common construct. So if people get in the habit of naming their refinements /ANY it would be kind of irritating. Every time you use it, you have to be careful to use lib.any for the control construct... or shuffle the names around to put ANY back and move the argument to a new variable.

The "/ANY" Behavior Is The Default For SET-WORD!

SET-WORD! doesn't error on "trash" assignments, it just clears the variable. That's pretty much firm.

It's a nice way to clear out variables:

 >> x: ~
 == ~  ; anti

It thus seems to make sense that SET of a WORD! would do what a SET-WORD! would do, and not error. So the /ANY would be superfluous by that logic.

GET Returning Nothing Doesn't Seem That Problematic (?)

Traditionally, the reason plain WORD! errors on unset variables is that you need to stop code from churning along when you thought you made a function call.

rebol2>> apend [a b c] [d e]
== [d e]  ; if unsets were ignored

There's no callee you are passing the value to who can reject the #[unset!].

But GET doesn't run functions. So you're always GET-ing the value to be putting or passing somewhere else.

A generic checker like MUST could be applied to say must get if you really wanted to enforce that you're getting something that wasn't nothing (or null? or void?) This could be useful in an assignment if you were trying to have error locality, since the SET-WORD! would accept a nothing without erroring:

other-name: must get @original-name

So... Do We Need The /ANY Switches On GET and SET ?

This has been on the table a long time (I edited this post from 2019 to bring it up to date, but the point remains).

Does anyone want to speak up in defense of their importance?

2019, huh?

:pouting_cat:

Well I think this aspect is sorted out. We control the GET or SET based on the decoration of what gets passed in.

>> x: ~#trashy~

>> $x
== x  ; bound

>> get $x
** PANIC: x is TRASH! (~#trashy~), must use ^META to GET

>> meta $x
== ^x  ; bound

>> get meta $x
== \~#trashy~\  ; antiform (trash!)

Doing it this way makes it easy for people writing dialects to "do the right thing according to decoration" with their SETs and GETs.

If their dialect had someone writing var: then they probably by default want plain assignment semantics. If they said ^var: they probably want meta-assignment semantics. (At least, this is the case for me, when I'm implementing something like UPARSE).

We don't want refinements that duplicate this configurability, because if the refinements contradict the decorations on the value itself, which do we listen to?

How Many Types Should GET and SET accept?

I've been leaning to saying not that many.

If you're doing a GET of @foo: what are the odds you know what you are talking about, such that we just throw up our hands and give you the same thing as GET of foo? And what happens when the day comes that we decide eval [@foo: 10] actually does something meaningful (that day may come soon!)

So I propose that GET and SET be pretty conservative in the sense of pick apart things in the way the evaluator would be willing to.

e.g. we've already got the machinery to handle something like [foo ^baz {bar}]: ^some-pack

So there's no point in being antagonistic and forcing people to write:

 >> my-block: $[foo ^baz {bar}]
 == [foo ^baz {bar}]  ; bound

 >> code: compose [(my-block): ('^some-pack)]
 == [[foo ^baz {bar}: ^some-pack]  ; bound

Instead, I'm proposing you should be able to say:

set my-block ^some-pack

And it runs through the same code path.


If the evaluator already does the thing, then expose the thing as a function that does it without the overhead of building a code block and instantiating an evaluator level.

But don't make up behaviors that haven't been condoned by the evaluator for "convenience". Factor the "convenience" part out with other tools, e.g. in functions like DECORATE

Framing it in reverse: if it is good enough for people to really need it programmatically in functions like SET and GET, it's probably good enough to have a syntax in the evaluator as well.


So if eval [@x: 10] does something, I'd expect set @x 10 to do it too. If it doesn't do anything, I don't expect SET to strip off the @ sign just for the sake of making it "easier" for someone who has an @x in a variable and feels like setting it. If the evaluator wouldn't know what it meant, the caller needs to take it off, or they'll get the same error they'd get if they tried that in the evaluator.

The nuance of if it should accept a trailing colon on the block is something likely worth making a concession for:

>> my-block1: $[foo ^baz {bar}]
== [foo ^baz {bar}]  ; bound

>> my-block2: $[foo ^baz {bar}]:
== [foo ^baz {bar}]:  ; bound

set my-block1 ^some-pack  ; legal
set my-block2 ^some-pack  ; concession: also legal?

This hinges on the idea that there's no such thing as [foo ^baz {bar}]::, because if there were, I would not advise making this concession.

And for this reason I would think that trying to SET :[foo ^baz {bar}] shouldn't strip off the leading chain, because you could write:

:[foo ^baz {bar}]: ^some-pack  ; ???

And I haven't decided if that should mean anything or not.

This Isn't Coming Completely Out of the Blue

I've been factoring the evaluator to take all the random historical uncommon paths and force them through chokepoints of common behavior.

It's feeling very solid and coherent to do so (even if it's leading to near term slowness).

So this is the direction I imagine it's best to take.

And get meta word looks better than get:any word any day.

1 Like