I pretty much consider being able to declare variables at the point they are first used to be a non-negotiable feature.
This is why I was a big fan of FUNCT when I first saw it. It looked for SET-WORD!s in your function and "gathered" them up implicitly as being local to that function.
funct [arg1 arg2] [ ; R3-Alpha "funct"
local1: 10
local2: 20
return (local1 + arg1) * (local2 + arg2)
]
=> func [arg1 arg2 /local local1 local2] [ ; equivalent R3-Alpha code
local1: 10
local2: 20
return (local1 + arg1) * (local2 + arg2)
]
I was one of the proponents for it taking over the relatively-useless role of FUNCTION, which was previously a fairly lame contraction like this:
function [arg1 arg2] [local1 local2] [...body...] ; Rebol2 "function"
=> func [arg1 arg2 /local local1 local2] [...body...] ; equivalent Rebol2 code
As you write more sophisticated code, you start to realize the weakness of automatically assuming SET-WORD!s are local. There are many uses of SET-WORD!s that are not implied as being local variables, and if dialect authors are to be free with using them as labels or other purposes then there will be many more.
I've written about the problems - Please Review if you haven't already.
I've experimented with LET as a replacement, but I'm not happy yet.
Right now, LET works mechanically about the same as SET-WORD!s did. let x: 10 behaves in a FUNC or FUNCTION body just as x: 10 did in a FUNCT(ION).
It's an improvement in the sense that it sacrifices a single word--instead of the whole class of values of SET-WORD!--for carrying the meaning of declaring a local.
There are questions about how this can interact with PARSE or other dialects, which had expanded their syntax e.g. to allow copy x: [some "a"] instead of just copy x [some "a"]. Should that now enable copy let x: [some "a"]? Who is responsible for these dialect syntax exceptions, where any point of providing a variable to assign might want to also declare that variable?
Beyond that... I have a nagging feeling that the way it works is wrong...injecting variables into the function definition and being scanned for. I feel like it should work more like a USE does, and be dynamically binding the code that comes after it when it is encountered. If you want to add variables to a function's definition you should always be able to do that with <local>
. But something tells me that LET should be a different beast...that uses a syntax trick to create a wave of binding that affects the statements after it instead of forcing your hand in making a code block the way USE does.
Can we punt on it?
Rebol2 got a fair way with just /local
. I think it would have been safer if it had unbound any SET-WORD!s in the body that were not explicitly declared or imported.
One of the main reasons this would be a pain would be OBJECT!s that would want methodized access to things without explicitly <with>
ing them. But Ren-C has METHOD to take care of this (and establishing the relationship to the object addresses other design holes, e.g. not having to deep copy and rebind all the functions in objects on each instance creation).
What if we went to a situation where you used <local>
for now, and all SET-WORD!s were unbound that weren't covered by either the args or <local>
or imported by <with>
or on an object supplied by <in>
(or implicitly via METHOD)? We could limit the "junk" which might accrue by having it so that <local>
s which do not have corresponding SET-WORD!s in the body raise an error.
I'm not floating the idea because I don't believe in being able to declare locals at their point of first usage. I'm suggesting it because I believe in the idea too much to see it done incorrectly, and what I've seen so far feels wrong.
Thoughts? I do like the idea of FUNC and FUNCTION being synonyms, and would like to stay the course with that direction.