Local values in Red are just words in function's context, so they can be set from outside.
function! datatype somehow limits access to them, but it is possible to set them anyway.
Let’s make some simple function with local value:
red>> f: func [/local word] [word] == func [word][word] red>> f == none
As you can see, local value is initialized to
none. But actually, it’s not a "real" local value, it’s a trick.
/local is refinement just as any other and it’s possible to call our function with that refinement:
red>> f/local 42 == 42
So do not expect that your local value will be always initialized to
none, user can override it. As I just demonstrated, local values are not safe.
Another thing, that will be important later is the
/local refinement. It’s actually just another word defined in the function context:
red>> f: func [/local] [local] == func [/local][local] red>> f == false red>> f/local == true
As you can see, when the refinement is used, it’s value is set to
local, you just don’t expect it to be used.
In previous examples we used scalar value. Things are bit different when we use
red>> f: func [value /local block] [block:  append block value] == func [value /local block][block:  append block value] red>> f 1 ==  red>> f 2 == [1 2]
This is something that is confusing for newcomers. Why is not
block empty on each call? Here’s the very importand difference between Red and some other languages. The source is not just parsed and executed, it is loaded. This is very important step that turns data into the code, in our case the source of the function into the function itself. When you are calling the function, it’s not loaded again, you are calling what was loaded into the memory when defining that function. Why is this important?
What you see above is an empty
block!. Like all
block! is passed by reference in Red. It happens automatically, you don’t have to care about it. But what it means in our case? We have
block:  in our function. When this code is loaded into memory, it is translated into something like
block: <reference to some block>. And this reference is there, in the body of our function and does not change. This is the reason, why the block is not empty on each call. Because on each call, it’s still the same
block!, that was created when the function was loaded.
Red, like Rebol3, has
map! type. If you are not familiar with
map!, it is basically key/value storage. Unlike Rebol3, Red has also literal
map! representation. To make an empty
map!, you can type
#(). As with the empty block above, I am going to put this on separate line:
The title of this article is Safe and persistent local values. I’ve already shown that locals can be persisent - sometimes against user’s will, when they don’t understand Red good enough yet - and by now, you probably already figured out how to prevent the problems described above and make locals safe. But for the reference, I am going to describe it anyway. So back to the literal
map! has basically the same properties as
block! described above as demonstrated in following example:
red>> f: func [word value /local map] [map: #() map/:word: value map] == func [word value /local map][map: #() map/:word: value map] red>> f 'a 1 == #( a: 1 ) red>> f 'b 2 == #( a: 1 b: 2 )
As you can see, local word
map always word refers to same
map!. Which is something we can use to make safe local values.
So back to local values. How can we make them safe (safe - user cannot change them)? Here’s some simple example of an attack on local values:
red>> f: func [/local val] [if val [#panic]] == func [/local val][if val [#panic]] red>> f/local #evil == #panic
The obvious and right solution to this problem is to initialize local value(s):
red>> f: func [/local val] [val: none if val [#panic]] == func [/local val][val: none if val [#panic]] red>> f/local #evil == none
But as you can see, we now have to define
val in the header and then initialize it in the body. There sure must be some easier way:
red>> f: func [/local] [local: #(val: #[none]) if local/val [#panic]] == func [/local][local: #() if local/val [#panic]]
NOTE: Because literal
map!is not reduced, we have to use literal
none!here (that is
#[none]), otherwise it would be
word!like any other. But that is something that I’m not going to explain here.
So now we can test it:
red>> f == none red>> f/local == none
So, what have we done? We defined just
/local refinement in the header. In the body,
map! was assing to the
word! and in that
map! we defined word
val. To access
val, we need to use
local/val, but as you can see, it is local to our function, even when not defined in the header:
red>> val *** Script error: val has no value *** Where: do
As said before, this could be done with initializing the
val value manually. But what couldn’t be done without literal
map! is the following example:
red>> f: func [/local] [ local: #(count: 0) local/count: local/count + 1 print ["function was called" local/count "time(s)."] ] == func [/local][local: #( count: 0 ) local/count: local/count + ... red>> f function was called 1 time(s). red>> f function was called 2 time(s). red>> f function was called 3 time(s).
We can use the
/local refinement, but it doesn’ make any difference:
red>> f/local function was called 4 time(s).
count is now really local and cannot be set and get from outside.
So now it’s time for some closure.
map!. It’s just bit more complicated.
countis safe now, right? Actually, no:
red>> m: second body-of :f == #( count: 4 ) red>> m/count: 42 == 42 red>> f function was called 43 time(s).