generalities of addresses and values...
[1/11] from: rishi::picostar::com at: 9-Oct-2000 18:55
I'm a bit confused on how references work in Rebol. I wish there was a chapter in the
rebol core manual dedicated on this alone. Perhaps someone could comment the general
way addresses and values work in Rebol.
Here is how I currently think it works...please correct me if I'm wrong.
-all one-level series are passed/copied/attached by value.
-all one-level objects are passed/copied/attached by value.
-all one-level functions are passed/copied/attached by value.
-all datatypes other than objects, series, and functions are passed/copied/attached by
value.
-all deep series are passed/copied/attached by reference.
-all deep functions (functions within functions or blocks within functions) are passed/copied/attached
by reference.
-all deep objects (objects within objects or blocks within objects) are passed/copied/attached
by reference.
did I miss anything?
Rishi
[2/11] from: larry:ecotope at: 9-Oct-2000 19:46
Hi Rishi
Well, there are a lot of issues raised by your questions. I am sure you will
get a number of responses. I will limit myself to a couple of quick
comments.
In many ways REBOL is closely related to the Scheme language.
1) In the Scheme community, pass-by-value means the arguments to a function
are evaluated by the interpreter before the function is called. This is true
of REBOL as well.
2) In Scheme, all (well almost all) values are (at least conceptually)
pointers (references). So an arg may be evaluated, but the value is a
pointer. REBOL is similar, but more complicated.
In Rebol, if a function has a string or block argument, the argument is
evaluated before the function is called. But strings and blocks evaluate to
themselves. In the case of blocks, the words within the block and
recursively in all nested blocks are added to the symbol global table by the
interpreter before the function is called, but no further processing is
done. The block is then assigned to the local word defined in the function
spec which is in the symbol table local to the function.
But what is really passed into the function and assigned to the local arg
word is a pointer to the string or block.
Here is a short example which illustrates this behavior.
>> s: "abcdef"
== "abcdef"
>> f: func [x][remove x]
>> f s
== "bcdef"
>> s
== "bcdef"
>> b: [1 2 3]
== [1 2 3]
>> f b
== [2 3]
>> b
== [2 3]
>>
>> x ;x is a word local to the function f
** Script Error: x has no value.
** Where: x
Notice that modifying the block or string referenced by the local variable
x, also changes the original global value. To prevent that you would have to
use COPY to create a new copy either in the function call or within the
function itself.
HTH
-Larry
----- Original Message -----
From: <[rishi--picostar--com]>
To: <[list--rebol--com]>
Sent: Monday, October 09, 2000 11:55 AM
Subject: [REBOL] generalities of addresses and values...
> I'm a bit confused on how references work in Rebol. I wish there was a
chapter in the rebol core manual dedicated on this alone. Perhaps someone
could comment the general way addresses and values work in Rebol.
[3/11] from: rishi:picostar at: 9-Oct-2000 22:59
ok. I think I get it now. Everything is basically done as a reference. Even setting words.
This is much simpler than what I was thinking :-)
ei:
a: 5
b: a
in this example, b is a reference to a (right?). The thing that was confusing me was
that the copy function doesn't always create a new copy. In deep series, it creates a
reference to copied value....strange...
By the way, is there any way to prove that b is a reference to a ( in the above example)
? I know you can do it if you set the value of as a series datatype (by using insert
or other series function). But how do you prove that b references a for number datatypes?
Rishi
Previously, you ([larry--ecotope--com]) wrote:
[4/11] from: sharriff:aina:med-iq at: 10-Oct-2000 7:18
Good question Rishi, thanks larry for sheding a little light on that
theme..
Sharriff Aina
med.iq information & quality in healthcare AG
Gutenbergstr. 42
41564 Kaarst
tel.: 02131-3669-0
fax: 02131-3669-599
www.med-iq.de
[5/11] from: brett:codeconscious at: 10-Oct-2000 18:39
> ok. I think I get it now. Everything is basically done as a reference.
Even setting words. This is much simpler than what I was thinking :-)
Um...
> a: 5
> b: a
>
> in this example, b is a reference to a (right?).
Nope. b is set with the result of evaluating a.
>> a: 5
== 5
>> b: a
== 5
>> a: 3
== 3
>> a
== 3
>> b
== 5
> The thing that was confusing me was that the copy function doesn't always
create a new copy. In deep series, it creates a reference to copied
value....strange...
> By the way, is there any way to prove that b is a reference to a ( in the
above example) ? I know you can do it if you set the value of as a series
datatype (by using insert or other series function). But how do you prove
that b references a for number datatypes?
I showed above that b is not a reference to a.
Here's another way to look at it.
You could set the word "b" to have a value which is literally the word "a" -
by doing this:
>> b: 'a
== a
You could then see the value of the word "b" by
>> get 'b
== a
or evaluting b
>> b
== a
and combining
>> get b
== 3
Which evaluates b to a value, in this case a word, and then applies the get
function to that value.
[6/11] from: joel:neely:fedex at: 10-Oct-2000 21:47
Hello, Rishi!
[rishi--picostar--com] wrote:
> ok. I think I get it now. Everything is basically done as a
> reference. Even setting words. This is much simpler than what
> I was thinking :-)
>
Well... maybe not!
> ei:
>
> a: 5
> b: a
>
> in this example, b is a reference to a (right?).
>
Not as I understand it. In the second line, the expression
a
is evaluated, yielding the value of 5. A copy of that value is
bound to b (in the current context) by
b:
Note: When I said "a copy" above, I wasn't pretending to know how
the REBOL interpreter works. I was trying to emphasize that all
copies of 5 are interchangeable.
> The thing that
> was confusing me was that the copy function doesn't always create
> a new copy. In deep series, it creates a reference to copied value
> ....strange...
>
I think you're talking about the difference between
b: copy a
and
b: copy/deep a
(If I'm mistaken here, please let me know!) The issue is that
series values are dealt with "by reference" so that one can do
>> a: ["This" "is" "a" "test"]
== ["This" "is" "a" "test"]
>> b: a
== ["This" "is" "a" "test"]
>> change next b "was"
== ["a" "test"]
>> a
== ["This" "was" "a" "test"]
The word a contains a reference to the literal, four-string
series in the first line. The second line binds to b a copy
of the value of a , which means that b now has a copy of
that reference. Since both of these references are to the same
data structure, changing the state of that structure (through
either reference) is visible via either/both.
Now consider one-level copying. With
>> a: ["This" "is" "a" "test"]
== ["This" "is" "a" "test"]
>> b: copy a
== ["This" "is" "a" "test"]
>> change next b "was"
== ["a" "test"]
>> a
== ["This" "is" "a" "test"]
>> b
== ["This" "was" "a" "test"]
it is (I hope) clear that THE BLOCK ITSELF was copied, not merely
a reference to it. But... what does the block contain? Well, it
contains four references! Blocks and strings are both series
values, which are handled via reference. So changing the value
to which b refers (as in the above example) doesn't affect
the value to which a refers, BUT going one level deeper and
changing a string (series value) to which both the (block) value
of a and the (block) value of b refer will again be visible
via either of the second-level references. Continuing after the
interaction above, we can do
>> change/part skip b/1 2 "at" 2
== ""
>> a
== ["That" "is" "a" "test"]
>> b
== ["That" "was" "a" "test"]
The way to ensure against such hidden connections is to do a deep
copy, which recursively copies nested series values.
>> a: ["This" "is" "a" "test"]
== ["This" "is" "a" "test"]
>> b: copy/deep a
== ["This" "is" "a" "test"]
>> change next b "was"
== ["a" "test"]
>> a
== ["This" "is" "a" "test"]
>> b
== ["This" "was" "a" "test"]
>> change/part skip b/1 2 "at" 2
== ""
>> a
== ["This" "is" "a" "test"]
>> b
== ["That" "was" "a" "test"]
After which we can see that
>> same? a b
== false
>> same? a/4 b/4
== false
>> same? a/4/1 b/4/1
== true
>> same? a/4/1 b/4/4
== true
because two strings that spell the same word are not necessarily
the SAME string. You can distinguish them by modifying one of them,
and seeing whether the other is modified. However, any two copies
of the letter #"t" are the same, because there's not any way to
reach inside
it and modify one of its parts.
> By the way, is there any way to prove that b is a reference to a
> (in the above example) ? I know you can do it if you set the value
> of as a series datatype (by using insert or other series function).
> But how do you prove that b references a for number datatypes?
>
It doesn't. If you wanted to prove that it did, you'd need to be
able to modify a part of a numeric value in-place and show the
change propagating. However, REBOL also does some other tricky
things in that regard. Consider:
>> a: 1-Jan-2000
== 1-Jan-2000
>> b: a
== 1-Jan-2000
>> same? a b
== true
>> b/2: 10
== 10
>> b
== 1-Oct-2000
>> same? a b
== false
>> a
== 1-Jan-2000
Even though you can modify a component of a date value, doing do
does not propagate. Therefore, one would conclude that the two
date values are considered the same because they are being tested
by value, rather than by reference. This is consistent with the
results below:
>> same? "test" "test"
== false
>> same? 1-Oct-2000 2000/10/01
== true
At this point, one would need to do similar tests for all REBOL
datatypes to completely answer your question. The description
of copy (with respect to the /deep refinement) leaves
the impression that anything under the series! pseudotype
would be a "reference", however.
>> series? ["This" "is" "a" "test"]
== true
>> series? "test"
== true
>> series? 1-Oct-2000
== false
The best advice I can give you is to forget the concept of "address"
that you may have from assembler, C, or any other low-level language.
There just isn't any way to get at the "storage location" associated
with a "variable" in REBOL -- in fact, I quoted those phrases just to
emphasize that I'm speaking a foreign language here! Those concepts
don't even exist in REBOL!
-jn-
[7/11] from: rishi:picostar at: 10-Oct-2000 22:29
I don't see how this proves that b is not a reference to a. Take this function for example:
test: func [x y /in][
either in [insert x y][x: y]
print ["in test function: " x]
]
; #1
X: 5
print ["x: 5"]
test x 10
print ["after: " x]
;output for #1
x: 5
in test function: 10
after: 5
;;#1 here it seems like the number x is passed by value since original x is not modified
;#2
x: "hello"
print ["x: hello"]
test x "bye"
print x
;output for #2
x: hello
in test function: bye
hello
;;#2 here it seems like the string x is passed by value since original string is not
modified.
;#3
x: "hello"
print ["x: hello"]
test/in x "bye"
print x
;output for #3
x: hello
in test function: byehello
byehello
;;#3 here I am totally baffled since in #2 it is passed by value, but here it is actually
modifying original value. passed by reference. This seems to contradict what you (brett)
said about 'b not being a reference of 'a. Example #3 shows that value in variable x
is referenced.
Why is #2 passed by value and #3 passed by reference? This seem very inconsistent to
me...but I'm sure reality is that I don't understand what is going on. It seemed to me
that Larry implied that everything in rebol is passed by reference. but does not seem
to be true as shown in above example. I don't know why this is so hard for me to grasp...
It shouldn't be more complicated than c pointers!
Rishi
Previously, you ([brett--codeconscious--com]) wrote:
[8/11] from: rishi:picostar at: 10-Oct-2000 23:37
Thanks Joel!! Please ignore (I know...too late) my previous message... Anyway, there
is still one piece of the puzzle missing..
>> a: "test"
== "test"
>> b: a
== "test"
>> print b
test
>> b: "hello"
== "hello"
>> print a
test
Here, when you set word 'b to 'a, I assume that 'a is dereferenced and then a copy of
that reference is placed in b. So now both 'b and 'a should point to the same structure.
Now when I set 'b to "hello", I change the address of the variable 'b to point to a new
structure.
oops...never mind...this is all making perfect sense now!!!
So, here is a third attempt (third time is a charm...right?) to generalize how addresses
and values work in rebol:
All series! datatypes are treated as references and all other datatypes (numbers, dates,
etc..) are treated as values. Has anyone confirmed this? I know I am not supposed to
snoop around and figure out how this stuff works in REBOL, but it is just something I
have to know...
Rishi
Previously, you ([joel--neely--fedex--com]) wrote:
[9/11] from: lmecir:geocities at: 11-Oct-2000 9:02
Hi,
Joel wrote:
> At this point, one would need to do similar tests for all REBOL
> datatypes to completely answer your question. The description
> of copy (with respect to the /deep refinement) leaves
> the impression that anything under the series! pseudotype
> would be a "reference", however.
>
For some info you can see:
http://www.rebol.org/advanced/mutable.r
and
http://www.geocities.com/lmecir.geo/evaluation.html
Regards
Ladislav
[10/11] from: joel:neely:fedex at: 11-Oct-2000 8:04
PMBI, but your example actually demonstrates the "series values are
reference values" issue nicely.
[rishi--picostar--com] wrote:
> I don't see how this proves that b is not a reference to a. Take this function for
example:
>
> test: func [x y /in][
> either in [insert x y][x: y]
> print ["in test function: " x]
> ]
>
Before testing it, let's think about what it does. There are two
locals x and y which are set from arguments. If the /in
refinement is used, the second is inserted into the first; otherwise
the first is bound to the second (in the context of the function!).
To make the context explicit, I'm going to refer to the arguments
as f.x and f.y (just making the context explicit -- sort of).
> ; #1
> X: 5
> print ["x: 5"]
> test x 10
> print ["after: " x]
>
So what we have is
x:5
; entering test [x 10]
f.x: 5 ; obtained from x
f.y: 10 ; obtained from literal
f.x: 10 ; obtained from f.y, /in was not used
print ["in test function: " 10]
; back from test [x 10]
print ["after: " 5]
> ;output for #1
> x: 5
<<quoted lines omitted: 6>>
> test x "bye"
> print x
What we should get is
x: "hello"
; entering test [x "bye"]
f.x: "hello" ; a copy of the reference in x
f.y: "bye" ; obtained from literal
f.x: "bye" ; f.x now refers the "bye" also
; referred to by f.y, /in was not used
print ["in test function: " "bye"]
; back from test [x "bye"]
print "hello" ; string referred to by x
> ;output for #2
> x: hello
<<quoted lines omitted: 6>>
> test/in x "bye"
> print x
What we should get is
x: "hello"
; entering test [x "bye"]
f.x: "hello" ; a copy of the reference in x
f.y: "bye" ; obtained from literal
insert "hello" "bye" ; the string to which f.x refers
; is modified to be "byehello"
; (that is the same string referred to by
x)
; because /in was used
print ["in test function: " "byehello"]
; back from test [x "bye"]
print "byehello" ; string referenced by x
> ;output for #3
> x: hello
> in test function: byehello
> byehello
>
> ;;#3 here I am totally baffled since in #2 it is passed by value,
>
--
Whoa! Which "it"?!
> but here it is actually modifying original value. passed by reference.
>
--
Again, which it?!
> This seems to contradict what you (brett) said about 'b not being a
> reference of 'a. Example #3 shows that value in variable x is
> referenced.
>
NO!!! Example #3 simply shows that f.x refers to the same string
as does x but NOT that f.x has anything to do with the global
variable x itself! Since #3 modifies the shared string, upon
return from test the modification is visible through x as it
still refers to the same string.
> Why is #2 passed by value and #3 passed by reference? This seem very
> inconsistent to me...but I'm sure reality is that I don't understand
> what is going on.
It is totally consistent. In both cases a reference to a string was
passed. In #2, that reference was simply REPLACED when the function
evaluated
x: y
(which I've been rewriting as
f.x: f.y
to emphasize that it those words are in the context of the function).
OTOH, in #3 that reference to a shared string was used to MODIFY THE
REFERRED-TO STRING.
> It seemed to me that Larry implied that everything
> in rebol is passed by reference.
>
If I were wording the situation from scratch, I'd say something like,
Everything in REBOL is passed by value. However, some REBOL values
ARE references -- to data structures which may be accessed and
modified using those references.
> but does not seem to be true as
> shown in above example. I don't know why this is so hard for me to
> grasp... It shouldn't be more complicated than c pointers!
>
Why not? REBOL is a different language than c, so why should it
behave like c?
If you really want to think in terms of a low-level, machine-oriented
language like c (which I do NOT advise), think of your example above
in terms of pointer variables. You cannot (in REBOL) get a pointer
TO a variable, but you can pass -- by value -- a copy of the pointer
that a variable CONTAINS.
For your function (reformatted with line numbers for explanation)
func [ ; f#01
x y /in ; f#02
][ ; f#03
either in [ ; f#04
insert x y ; f#05
][ ; f#06
x: y ; f#07
] ; f#08
print [ ; f#09
"in test function: " ; f#10
x ; f#11
] ; f#12
] ; f#13
we can evaluate
x: "hello" ; g#01
test x "bye" ; g#02
print x ; g#03
x: "bye" ; g#04
test/in x "good" ; g#05
print x ; g#06
using the following pictures (pardon the clumsy ASCII art!).
This is NOT standard REBOL notation, but just a way to try
to depict one possible model of what's happening. The arrows
show references, the {{...}} show internal data structures.
I'll keep the f.x and f.y notation as well, to make
clear which words we're talking about. (I don't want to try
to draw contexts -- these pictures will be cluttered enough!)
Each picture represents the state of the relevant variables
after the statment has completed.
g#01:
x = {{string! 1 *}}
|
V
{{#"h" #"e" #"l" #"l" #"o"}}
Now x refers to a string (at position one) containing
five characters.
g#02/f#03: (i.e., just upon entry to the function...)
x = {{string! 1 *}}
|
V
{{#"h" #"e" #"l" #"l" #"o"}}
^
|
f.x = {{string! 1 *}}
{{#"b" #"y" #"e"}}
^
|
f.y = {{string! 1 *}}
f.in = {{none!}}
Now the VALUE of f.x is an identical COPY of the VALUE
of x ; both refer to same position of the same five
character sequence. The value of f.y is a different
string entirely.
Since the /in refinement was not used in the call, its value
is none , which means we'll skip ahead to f#07...
g#02/f#07
x = {{string! 1 *}}
|
V
{{#"h" #"e" #"l" #"l" #"o"}}
f.x = {{string! 1 *}}
|
V
{{#"b" #"y" #"e"}}
^
|
f.y = {{string! 1 *}}
f.in = {{none!}}
The VALUE of f.x has been changed. Now it is a copy of
the value of f.y which was initialized to a different
string.
g#02/f#12
Since the phrase beginning in f#09 refers to f.x , we
get the side effect and final state...
(output) in test function: hello
x = {{string! 1 *}}
|
V
{{#"h" #"e" #"l" #"l" #"o"}}
f.x = {{string! 1 *}}
|
V
{{#"b" #"y" #"e"}}
^
|
f.y = {{string! 1 *}}
f.in = {{none!}}
and resume activity at the global level after g#02, at which
point we don't have any access to the context of the function
g#02
(output) in test function: hello
x = {{string! 1 *}}
|
V
{{#"h" #"e" #"l" #"l" #"o"}}
Now we can continue with the next global statements
g#03
in test function: hello
(output) hello
x = {{string! 1 *}}
|
V
{{#"h" #"e" #"l" #"l" #"o"}}
g#04
in test function: hello
(output) hello
x = {{string! 1 *}}
|
V
{{#"b" #"y" #"e"}}
g#05/f#03
in test function: hello
(output) hello
x = {{string! 1 *}}
|
V
{{#"b" #"y" #"e"}}
^
|
f.x = {{string! 1 *}}
{{#"g" #"o" #"o" #"d"}}
^
|
f.y = {{string! 1 *}}
f.in = {{true!}}
Now the VALUE of f.x is an identical COPY of the VALUE
of x ; both refer to same position of the same five
character sequence. The value of f.y is a different
string entirely.
Since the /in refinement WAS used in the call, its value
is true , which means we'll skip ahead to f#05...
g#05/f#05
in test function: hello
(output) hello
x = {{string! 1 *}}
|
V
{{#"g" #"o" #"o" #"d" #"b" #"y" #"e"}}
^
|
f.x = {{string! 1 *}}
{{#"g" #"o" #"o" #"d"}}
^
|
f.y = {{string! 1 *}}
f.in = {{true!}}
The insert action used the string reference of f.x to
modify the string's value. Since the value of f.x was
an identical COPY of the VALUE of x , both references still
provide access to the modified character sequence.
g#05/f#12
in test function: hello
hello
(output) in test function: goodbye
x = {{string! 1 *}}
|
V
{{#"g" #"o" #"o" #"d" #"b" #"y" #"e"}}
^
|
f.x = {{string! 1 *}}
{{#"g" #"o" #"o" #"d"}}
^
|
f.y = {{string! 1 *}}
f.in = {{true!}}
and we can now resume global activity after g#05 (ignoring the
inaccessible context of the function)...
g#06
in test function: hello
hello
in test function: goodbye
(output) goodbye
x = {{string! 1 *}}
|
V
{{#"g" #"o" #"o" #"d" #"b" #"y" #"e"}}
Conclusion: Ignoring one level of indirection is fatal to an
understanding of what's going on. Trying to make REBOL behave
like a different language is fatal as well. Notice that in none
of the pictures above did we have any arrows pointing to variables.
While one may be able to draw such a picture, it has no meaning
in REBOL.
Hope this helps!
-jn-
[11/11] from: joel:neely:fedex at: 11-Oct-2000 9:22
[rishi--picostar--com] wrote:
> All series! datatypes are treated as references and all other
> datatypes (numbers, dates, etc..) are treated as values.
>
That is my understanding, given the way you are using the terms
reference
and "value". It could also be said that series values
are mutable, while other values are immutable.
> Has anyone confirmed this?
>
I haven't had time to do all the "REBOL particle physics" myself,
and don't know whether anyone else has tried all the possibilities
either.
> I know I am not supposed to snoop around and figure
> out how this stuff works in REBOL, but it is just something I have
> to know...
>
To the contrary! You SHOULD "snoop around" and confirm for yourself
that you understand how things are behaving in REBOL! That's likely
the best way to learn, and to confirm that what you think you've
learned is true.
Keep REBOLving!
-jn-
Notes
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted