Rethinking Auto-Gathered SET-WORD! Locals

Having to explicitly declare locals at the top of your FUNC is certainly primitive, so there needs to be another answer.

It's important to have a way to annotate variables as being local declarations at the place you use them.

...but...I think that the decision to consider this annotation to be implicit with a SET-WORD! in R3-Alpha's FUNCTION is probably a bad idea (...for anything but a code golf language).

It creates too many spurious locals

There are many reasons you might use SET-WORD!s and not all of them warrant a local. We say these are generic dialecting parts, they don't have to mean anything related to assignment at all (though it helps if they are, usually). But even common assignment-related usages make a bunch of stray locals:

something: ...  ; some "global" thing you want to reference

foo: function [...] [
    something  ; this is null because a something: was gathered below!

    obj: make object! [
        field1: ...  ; why would FOO now have a <local> named field1 ?
        field2: ...  ; and why now a <local> named field2 ?
    ]

    bar: function [...] [
         something: ...  ; this is a local in BAR, but why in FOO too??
    ]
 ]             

Gathering locals you don't need is inefficient. You're using more storage space for the function's frame, for a variable you're never going to use. It makes the function slower to call, and also looks confusing if you look in the debugger to see a bunch of spurious locals for the frame.

It's also inconvenient--because as with something above, it may cause a variable to be nulled out that you actually had meant to use.

I think we should require an annotation

It doesn't seem to me to be too terrible to have people say let x: 10 or var x: 10 instead of just x: 10. (I'll suggest var for today's method because it would get effectively "lifted", and I have some more interesting "virtual binding" ideas for let)

Like I say, it's not that something like an auto-gathering FUNCTION just from a SET-WORD! shouldn't exist. The whole point of Rebol is that you can make such things if you want them. But the idea strikes me as flawed enough in most applications that you should do some extra typing.

Then, what we can do is just say that any SET-WORD! which aren't covered by this get unbound. If they try to run in the evaluator, you get an error. (UPDATE: See thoughts on issues with this idea)

Not a big change so much as naming change

We'll just have to come up with a name or setting for the gathering function.

Preferably not FUNCT (which is what Rebol2 called the gatherer). :slight_smile:

Having a way to express a desire to auto-gather locals in the spec, e.g. <local> #set-word! or <autolocal> or something like that is a possibility.

Questions/Comments/Concerns?

In the annotating proposal I'm making here, VAR would be scanned for by name by FUNCTION. This would make it a sort of "keyword" in the "function body dialect" :-/ (though perhaps you could override it, e.g. by saying <varword> myvar in the spec).

But I don't suggest removing it from the body. I suggest leaving it there. But then, it could have a behavior at runtime like this:

>> var x: 10
== 10

>> var [x y z]: [10 20 30]
== [10 20 30]

>> var x
== x

>> var [x y z]
== [x y z]

So it would be a variadic which would look at its right, see if it was a SET-WORD! or a SET-BLOCK!, and if so run one unit of evaluation and return the result. If it were a WORD! or a BLOCK! it would return the word or block as is...acting like LIT would.

This would give you the ability to do something like both declare a local and SET it, as in:

set var x do whatever

Because it would be looked for literally, it would suffer the same problem that source-level vs. generated set-words do today...

 foo: function [] [
     do compose [
         var (to word! "wontwork"): 10
         print [wontwork]
     ]
 ]

But this is where I'm hoping that the implementation tricks I have in mind for let would be able to work.

A post was merged into an existing topic: Taking ACTION! on "Function vs. Action"

Something about Rebol makes VAR a bad name for this. That is, you frequently have a declaration which holds a WORD! or a PATH! and names a variable. VAR is a good name for that (e.g. the X parameter in for-each x [a b c] [...] is the kind of thing you would call var as the literal name of the variable.)

Given how common that is, using LET or repurposing USE makes more sense.

I think getting rid of auto-gathering of SET-WORD! is important

Since this has crossed my mind, everything I look at makes me think that picking a multi-purpose dialecting part like SET-WORD! for this purpose is a mistake. There's just too many ways you want to use them unrelated to declaring locals.

Whereas stealing one word as being uniquely partnered with FUNCTION (as how KEEP is partnered to COLLECT) is a much less consequential theft. Especially if you could override it with something else as part of the function spec dialect.

This is another one of those "daunting" change proposals, and the impacts on PARSE in particular bear scrutiny. (Should it be copy let x: [some integer!] or let x: copy [some integer!]). But just mentioning that thinking about it hasn't gone away... and that I don't think VAR is what it should be called due to frequent other uses for that term.

I should mention that going this direction gives us what I consider to be a good change: FUNC as a simple contraction for FUNCTION. They will be the same thing.

This is in the spirit of EVAL being made a synonym for EVALUATE and other ways of avoiding confusion.

1 Like

A year later, and we actually have virtual binding. So...

The process is now underway, first with METHOD...and then with FUNC. We'll use METH and FUNC while METHOD and FUNCTION are phased out in their old meaning. Then--when enough time has passed--you'll be able to say METHOD and FUNCTION as synonyms for the behavior that METH and FUNC have.

I'm well aware that the LET implementation is not perfect. But what it's replacing was an unfixably broken design choice. And what is there so far of LET shows enough promise to be enough of a better bet that we try pushing it and see what it can and can't do.

1 Like

It's been quite a number of years now since I deemed locals-gathering-FUNCTION to be unfit.

But it has been lingering around.

One reason is because I had never quite figured out how to make LET work in PARSE. Which means I have to change:

foo: function [...] [
    parse [
        alpha: across some ...
        beta: collect ...
        ...
        zeta: try block! ...
   ]
]

into

foo: func [...] [
    let alpha
    let beta
    ...
    let zeta
    parse [
        alpha: across some ...
        beta: collect ...
        ...
        zeta: try block! ...
   ]
]

Besides this, there's also the fact that LET's implementation is still very experimental and sketchy. So it's not quite as robust as <local>...

But With Pure Virtual Binding II, Locals-Gathering Is Finally Dead!

For one thing, there's now a LET in UPARSE.

For another thing, the idea of LET being based on an accrued environment is being committed to. Environments accrue and chain, and so LET is one of the things you should be able to do.

In any case... the eventual idea is that FUNC will just be shorthand for FUNCTION, with the two meaning the same thing. (see Abbreviations as Synonyms)

1 Like