Comments on: About /local words
There's been some consternation regarding the use of the /local refinement for passing initial values to internal variables.
I'd have to admit that I didn't think this was an important issue... because all good functions initialize critical locals.
However, that's not true, is it? That's the old C programmer in me thinking in that way. Most of us REBOLers know that locals are initialized to NONE, so we depend on that fact.
The problem occurs when we use such uninitialized NONE values to produce important effects within our code.
I'm very much against requiring programmers to write code like this:
doit: func [arg1 /local loc1 loc2][
loc1: loc2: none
...
]
So, although the REBOL interpreter has no internal recognition of the /local refinement, perhaps it should.
Of course, any such change has a minor impact on performance, although it should be unnoticeable. It might also be possible to compensate for the extra check by using /local to terminate the function arg parse. In other words, /local must be the last refinement within the function spec. Any other's that follow it will be ignored.
Post your comments. We need to address this issue immediately.
20 Comments Comments:
Brian Hawley 13-Oct-2010 13:35:18 |
It's really not a problem to put a assert [not local] at the beginning of the function, but the trick is remembering or realizing that you need to do so. Also, local is used as a temporary local variable by a lot of functions instead of using an extra tmp variable, so that would still need to work.
If we require /local to be the last refinement, there is one code trick that I do on occasion that won't work anymore: private refinements. These are function options that are not meant to be part of the public API, but are needed for internal use. I have used these in the past with functions that control access to data, but might need to provide more access to the data under certain special circumstances. For instance, I made self-updating functions with data migration.
In R3, you can accomplish these same tricks by encapsulating the data in a module, providing multiple functions to access it, and only exporting the public API to the lib. The private API can be unexported, or exported from a mixin.
The point is that whatever obscure functionality we lose by this, we have alternate methods for accomplishing the same thing. Still, that is one tradeoff to consider. Does anyone else have some /local tricks that they rely on? | Maxim Olivier-Adlhoch 13-Oct-2010 14:04:50 |
I have used /local as a generic data place-holder many times... this should still work...
in fact, I'd go even further and say that /local should ALWAYS exist after a func builder is done.
this is very useful in situations where we don't have access to the function builder itself... a good example would be gui 'action blocks or the init block in R2 faces.
With a local word always defined, we have a generic way of storing temp data without the risk of affecting external contexts. | Gregg Irwin 13-Oct-2010 16:27:26 |
Keep it simple for us; we shouldn't have to think like C programmers.
Avoid tricks, including reusing 'local just to save a word.
The fact that /local is just a convention can be confusing when you learn about it, but the function dialect was designed this way for a reason, right? Knowing what we know now, and given the changes in the computing world, I would first ask what a modern function dialect should look like. The current dialect is a bit "soft", without an official grammar I'm aware of. That makes writing code analysis tools harder, or at least harder to know if they're compatible with what REBOL thinks is true.
Give me a spec, tell me why each piece is the way it is (and what it does), and I'll be happy. It also gives us a concrete foundation for discussion. | Endo 14-Oct-2010 4:39:16 |
+1 for Gregg. | DideC 14-Oct-2010 7:34:42 |
I read this post twice... then a third time...
...and I still don't understand what is the point/problem/question !
Words following /local in a function header are set to NONE. Dot.
>> a: func [/local a b c] [print [a b c]]
>> a
none none none
So is this in R2 or R3. I know that, most of us know that or will learn it early. It's fine like this.
If I need another value, I initialize the word with it in the function code. KISS!
| Sunanda 14-Oct-2010 12:54:54 |
DideC -- you are missing that /local is a valid refinement when invoking the function:
>> a/local 1 2 3
1 2 3 | Oldes 15-Oct-2010 19:07:13 |
Sunanda -- that's a fact which I have been missing as well:)
Btw, in my code I always use /local (if I use it) as the last refinement within the function spec and I've never used it for anything else. Maybe it's because of the fact, that I considered /local as something more special. | meijeru 16-Oct-2010 3:51:31 |
HELP also relies on /local to be the last refinement. | Brian Hawley 16-Oct-2010 13:41:18 |
Meijeru, that is actually not so. The help function just assumes that /local is the last refinement, and ignores everything from that position on, including any other refinements. These hidden refinements can be used as a private API, as I mentioned above. It's a fun trick, but I am willing to go without it. | Ladislav 18-Oct-2010 2:49:15 |
Please, don't change the way it works now, we got used to it. | Mark Ingram 18-Oct-2010 10:58:21 |
+100 Ladislaw, it is perfect that /local is just like any other bare-word refinement.
If there is a huge demand for preventing the caller from passing in values to variables listed in the spec block, then I respectfully suggest using one of the other, currently broken, refinements.
Up to now I have only tried numerical ones like /0 -- out of sheer terror when I saw what happened with them (we need a grammar, Carl, sorry) -- but I have an active enough imagination to envision a world in which for example /:none does what is wanted here, i.e., it has meaning in the spec block but is an error if used as a refinement in the call.
| Paul personne 20-Oct-2010 3:41:20 |
I am back with my old fads ... I still think that all the "setting" functions should act in the local context and that if necessary the variables should be exported to other contexts (including global) With A export/context/type/value function ... | Oldes 20-Oct-2010 5:09:37 |
Paul, if you want to define function with all set-words as local, you can use FUNCT in REBOL3.
>> ? funct
USAGE:
FUNCT spec body /with object
DESCRIPTION:
Defines a function with all set-words as locals.
FUNCT is a function value.
ARGUMENTS:
spec -- Help string (opt) followed by arg words
(and opt type and string) (block!)
body -- The body block of the function (block!)
REFINEMENTS:
/with -- Define or use a persistent object (self)
object -- The object or spec (object! block! map!)
| Oldes 20-Oct-2010 5:23:50 |
Just noticed, that since version a108 there is a new FUNCT's /extern refinement, so it's even more powerful.
>> a: 1
== 1
>> f: funct[][a: 2 b: 3]
>> f
== 3
>> a
== 1
>> b
** Script error: b has no value
>> a
== 1
>> f: funct/extern[][a: 2 b: 3][a]
>> f
== 3
>> a
== 2
| Paul Personne 20-Oct-2010 15:56:23 |
thanks oldes, but i still use 2.77 because of lack of view in R3 for macintosh.
And i think local should be the default mode. | Sunanda 26-Nov-2010 17:51:06 |
There appears to be a simple way of making local variables untouchable by the calling function.....Replace /local with /1 (or any other integer):
Use local, and the variable is compromised:
f: func [aa /local bb] [print [aa bb]]
f/local 11 22
11 22
Use /1 and it is safe:
f: func [aa /1 bb] [print [aa bb]]
f 11 22
11 none ;; value not passed
== 22
f/1 11 22 ;; value cannot be passed in with /1 refinement
** Script error: incompatible refinement: 1
This seems to work in both R2 and R3.
| Brian Hawley 26-Nov-2010 18:54:24 |
Sunanda, you can still pass in stuff to functions with numeric refinements using apply, in R2/Forward and R3. Plus you can use manually created paths (rather than literal paths) - that is what R2/Forward's apply does.
There is a bug ticket about numeric refinements, but it is debatable as to whether we should disallow them just because they are difficult to put in path syntax. Not all dialects translate refinements to paths; some use them directly. | Brian Hawley 2-Jan-2011 20:58:02 |
Paul, R2 also has funct/extern as of 2.7.8.
We can't have local be the default in make function! for various extremely technical reasons that are inherent in the syntax and semantics or REBOL - there was a long discussion of this in CureCode, which I won't repeat here. However, funct has been in R2 since 2.7.7, and it manages local-by-default quite easily. | Sunanda 24-Feb-2011 13:26:12 |
One possible approach, that stops all but the most determined from passing /local values into a function:
In the absence of a type-specifier on the /local word, change the interpreter to assign a default of [unset!] -- ie
f: func [/local a b] [print [local a b]]
is treated as if it were coded:
f: func [/local [unset!] a b] [print [local a b]]
This appears to stop the APPLY trick:
>> apply :f [1 2 3]
** Script error: -apply- does not allow logic! for its /local argument | Louis Vuitton bags outle 12-Jul-2012 23:26:09 |
Visit buyIf want to know where you want to buy Louis Vuitton bags outlet sale, you can use online resources Designer Louis Vuitton bags outlet sale visit descriptions of the Louis Vuitton bags and the big guy for the different costs and other accessories. You can find the online destination and Overstock Handbagcrew reduced price handbags and designer handbags. Check out other great creators of these pages that have the same quality and you will be able to see, how much to save - prices really have to pay a department store shopping at this site is much more normal. Have fun while you shop Louis Vuitton bags outlet sale! |
Post a Comment:
You can post a comment here. Keep it on-topic.
|