[REBOL] Re: Context misery
From: joel:neely:fedex at: 5-Jun-2001 7:38
Hi!
[Sanghabum--aol--com] wrote:
> Hi there,
>
> Could someone with a better grasp of context than me explain why this won't
> work?---I think the problem is using a local variable in a DO:
>
> myfunc: func [BlockName [string!]
> /local aa bb][
>
> aa: copy [1 2 3]
> bb: join BlockName ": copy aa"
> print ["aa is " mold aa]
> print ["bb is " mold bb]
> do bb
> ]
>
> When I run it I get:
> >> myfunc "myblock"
> aa is [1 2 3]
> bb is "myblock: copy aa"
> ** Script Error: aa has no value
> ** Where: myfunc
> ** Near: myblock: copy aa
> >>
>
The REBOL/Core Users' Guide says (page 3-11):
Unless it is necessary, evaluating strings is generally not
a good practice. Evaluating strings is less efficient than
evaluating blocks, and the context of words in a string is
not known.
I strongly recommend taking that advice to heart! Trying to
construct source code strings that DO what you want is tricky
at best, and a potential source of highly subtle bugs at
worst.
That said, when you define BB using the expression
bb: join BlockName ": copy aa"
and then DO that string, REBOL has no idea that you intend for
the two-letter token "aa" in the string to indicate the LOCAL
word AA in the current function. (Without going into deep and
opaque waters, just remember that there can be *many* words
whose names are spelled the same!)
To get the value that's in your local variable to be the
value stored in the (possibly!) new variable named by the
BLOCKNAME parameter, you will need to force evaluation of
your local AA and then use that result to construct the
string. One possible (and possibly clumsy) way to do this is:
>> myfunc: func [BlockName [string!] /local aa bb] [
[ aa: copy [1 2 3]
[ bb: rejoin [BlockName ": copy " mold aa]
[ print ["aa is " mold aa]
[ print ["bb is " mold bb]
[ do bb
[ ]
>> myfunc "myblock"
aa is [1 2 3]
bb is "myblock: copy [1 2 3]"
== [1 2 3]
>> myblock
== [1 2 3]
>>
But *please* remember the old joke: "Doc, it hurts when I
do this!" "Well, then don't do that!" ;-)
> All I'm trying to do is create a variable dunamically based
> on a name passed to a function.
>
Why? If you can describe what you're trying to accomplish
here, perhaps some of the old hands can offer some alternatives
that won't be so tricky to deal with. For example...
You can think of REBOL contexts as being like mini-dictionaries
that map strings (the word names) to other values. (Now you
see why different contexts can have words with the same name!)
If you're trying to keep up with some data and you *really*
need to associate values with names you can't know in advance,
you could build your own "dictionary" as follows:
fw: make object! [
storage: copy []
learn: func [
token [string!] value [any-type!]
/local where
][
either none? find storage token [
append storage reduce [token value]
][
change/only storage value
]
]
fetch: func [token [string!]] [
select storage token
]
]
>> fw/learn "myblock" copy [1 2 3]
== ["myblock" [1 2 3]]
>> fw/learn "yourblock" copy [4 5 6]
== ["myblock" [1 2 3] "yourblock" [4 5 6]]
>> fw/learn "anyblock" [9 8 7]
== ["myblock" [1 2 3] "yourblock" [4 5 6] "anyblock" [9 8 7]]
>> fw/fetch "myblock"
== [1 2 3]
This is just a Q&D sample; an obvious improvement is to have
LEARN return the VALUE argument as its result to facilitate
embedding in larger expressions. Implementing that, and any
other improvements, is left as an exercise to the reader! ;-)
Hope this helps!
-jn-
------------------------------------------------------------
Programming languages: compact, powerful, simple ...
Pick any two!
joel'dot'neely'at'fedex'dot'com