objects without overhead
[1/16] from: rishi:picostar at: 17-Oct-2000 21:44
I am working on a program which requires about 1000 or so instances of objects. It doesn't
have to be OO but I like the design better that way. I am using a function as constructor
like the following example (not real code..):
;I used dashes to keep formatting intact (hopefully this works)
make_myobject: func[var1 var2 var3][
--return make object! [
----instancevar1: var1
----instancevar2: var2
----instancevar3: var3
----instancefunc1: func[][...]
----------------.
----------------.
----------------.
-]
]
The thing I don't like about this solution is that every instance of this object has
duplicate copies of the functions (As far as I know...please correct me if I am wrong).
This seems like a waste to me - especially if the object has 50 or so functions. Is there
any OO way in Rebol to create multiple instances of this object without creating multiple
copies of the functions associated with each object? I know how to do this in a non-object
oriented fashion but would like to see an OO solution.
[2/16] from: rishi:picostar at: 17-Oct-2000 23:08
Well...I thought about this a bit and I THINK I found an elegant solution. So I guess
I'll respond to my own question...
I think the solution to this problem is inner objects. Here is an example:
myobject: make object! [
- function1: func[][..]
- function2: func[][..]
- .
- .
- make: func[var1 var2 var3][
- return make object! [
- instancevar1: var1
- instancevar2: var2
- instancevar3: var3
-
- instancefunc1: function1 ;copies a reference to function1 above...
- instancefunc2: function2
- ]
- ]
]
;;;create instance of object
coolobj: myobject/make
coolobj/instancfunc1 ; all instances access same function! less overhead if many objects!!!
.
.
.
I am not sure if this would work since I have never tested it (yet). The questions in
my mind are:
1. are functions copied by reference or value? if by value, this would not solve my problem
2. do inner objects have access to outer object words? for example, can 'instancefunc1
access 'function1 as shown above?
I hope this works. If it does, it would be a very simple but elegant solution to my problem.
It would be even more elegent when modules come around so I can keep all private words
in context of myobject rather than the actual object returned...making code very clear.
Rishi
Previously, you ([rishi--picostar--com]) wrote:
[3/16] from: al:bri:xtra at: 18-Oct-2000 1:02
You could try something like this:
Outer: make object! [
Inner: make object! [
Data1: string!
Data2: integer!
]
Data1: func [Inner [object!]] [
Inner/Data1
]
Data2: func [Inner [object!]] [
Inner/Data2
]
Make-Inner: func [Block [block!]] [
clone Inner Block
]
set 'Dialect func [Block [block!]] [
make object! bind Block 'self
]
]
Dialect [
Zot: Make-Inner [
Data1: "one"
Data2: 2
]
Zot2: Make-Inner [
Data1: "111111"
Data2: 22
]
print Data1 Zot
print Data2 Zot
print Data1 Zot2
print Data2 Zot2
]
Which, after pasting into the console, gives:
one
2
111111
22
Andrew Martin
In, out and between Rebolutionary...
ICQ: 26227169
http://members.nbci.com/AndrewMartin/
[4/16] from: rishi:picostar at: 18-Oct-2000 2:04
There were a few mistakes in my previous emails code. I tried out the basic concept though...but
it was a failure. here's a simplified version of code. This is real code and I have run
it through rebol:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CODE BEGINS HERE
state: make object! [
new: func [
puzzle [block!]
][
return make object! [
puz: copy reduce puzzle
get-puzzle: :get_puzz
]
]
get_puzz: does [return copy puz]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;: TEST CODE
; comment {
test-obj: new [1 2 3 4 0 5 6 7 8]
print test-obj/get-puzzle
; }
]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CODE ENDS HERE
Now if you copy this code and run it, you get the following error:
** Script Error: puz has no value.
** Where: return copy puz
>>
I was really hoping this would work. It seemed like an elegant solution. I don't understand
why get-puzzle cannot access word 'puz.
Previously, you ([rishi--picostar--com]) wrote:
[5/16] from: al:bri:xtra at: 18-Oct-2000 3:10
Rishi wrote:
> I was really hoping this would work. It seemed like an elegant solution.
>> state: make object! [
[ new: func [
[ puzzle [block!]
[ ][
[ make object! [
[ puz: copy reduce puzzle
[ get-puzzle: :get_puzz
[ ]
[ ]
[ get_puzz: does [return copy puz]
[ ]
>> test-obj: state/new [1 2 3 4 0 5 6 7 8]
>> probe test-obj
make object! [
puz: [1 2 3 4 0 5 6 7 8]
get-puzzle: func [][return copy puz]
]
>> test-obj/get-puzzle
** Script Error: puz has no value.
** Where: return copy puz
> I don't understand why get-puzzle cannot access word 'puz.
state/get_puzz is not in the same context as test-obj/puz.
Andrew Martin
ICQ: 26227169
http://members.nbci.com/AndrewMartin/
-><-
----- Original Message -----
From: <[rishi--picostar--com]>
To: <[list--rebol--com]>
Sent: Wednesday, 18 October 2000 3:04 PM
Subject: [REBOL] objects without overhead Re:(2)
> There were a few mistakes in my previous emails code. I tried out the
basic concept though...but it was a failure. here's a simplified version of
code. This is real code and I have run it through rebol:
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CODE BEGINS HERE
> state: make object! [
<<quoted lines omitted: 19>>
> >>
> I was really hoping this would work. It seemed like an elegant solution. I
don't understand why get-puzzle cannot access word 'puz.
>
> Previously, you ([rishi--picostar--com]) wrote:
> > Well...I thought about this a bit and I THINK I found an elegant
solution. So I guess I'll respond to my own question...
> >
> > I think the solution to this problem is inner objects. Here is an
example:
> >
> > myobject: make object! [
<<quoted lines omitted: 9>>
> > -
> > - instancefunc1: function1 ;copies a reference to function1
above...
> > - instancefunc2: function2
> > - ]
<<quoted lines omitted: 4>>
> > coolobj: myobject/make
> > coolobj/instancfunc1 ; all instances access same function! less
overhead if many objects!!!
> > .
> > .
> > .
> > I am not sure if this would work since I have never tested it (yet). The
questions in my mind are:
> > 1. are functions copied by reference or value? if by value, this would
not solve my problem
> > 2. do inner objects have access to outer object words? for example, can
'instancefunc1 access 'function1 as shown above?
> >
> > I hope this works. If it does, it would be a very simple but elegant
solution to my problem. It would be even more elegent when modules come
around so I can keep all private words in context of myobject rather than
the actual object returned...making code very clear.
> >
> > Rishi
> >
> >
> > Previously, you ([rishi--picostar--com]) wrote:
> > > I am working on a program which requires about 1000 or so instances of
objects. It doesn't have to be OO but I like the design better that way. I
am using a function as constructor like the following example (not real
code..):
> > >
> > > ;I used dashes to keep formatting intact (hopefully this works)
<<quoted lines omitted: 14>>
> > >
> > > The thing I don't like about this solution is that every instance of
this object has duplicate copies of the functions (As far as I know...please
correct me if I am wrong). This seems like a waste to me - especially if the
object has 50 or so functions. Is there any OO way in Rebol to create
multiple instances of this object without creating multiple copies of the
functions associated with each object? I know how to do this in a non-object
oriented fashion but would like to see an OO solution.
[6/16] from: joel:neely:fedex at: 18-Oct-2000 7:22
Hi, Rishi,
[rishi--picostar--com] wrote:
> state: make object! [
> new: func [
<<quoted lines omitted: 9>>
> print test-obj/get-puzzle
> ]
[snip]
> I don't understand why get-puzzle cannot access word 'puz.
>
Look at the following (awful ASCII art) diagram, which shows the
contexts and references of the various names involved:
(global)
|
+--------------...--+
| |
state==>(object!) (etc)
|
+--------+-----------+
| | |
new | test-obj==>(object!)
| | |
puzzle | +----------+
| | |
get_puzz<==get-puzzle puz
As the above code is being evaluated, look at the contexts that
are established, and the words they contain.
Context for... Contains words....
---------------- ---------------------------------------------
(REBOL global) 'state
(along with whatever else was there)
STATE 'new 'get_puzz 'test-obj
(the object's "members")
NEW 'puzzle
(the function's argument)
Now, the next-to-last line inside STATE makes 'test-obj (in
the context of STATE) refer to a newly-created object which
has the following context:
TEST-OBJ 'get-puzzle 'puz
Here's the critical issue: TEST-OBJ refers to an object whose
component GET-PUZZLE refers to the *value* of GET_PUZZ, which
contains words defined *in the context of its own definition*
and in that context, *there is no 'puz*. Remember that 'puz
is defined in the context of the object to which TEST-OBJ now
refers, but *not* in the context of STATE.
If you'll pardon the anthropomorphism, TEST-OBJ knows both
GET-PUZZLE and PUZ, and introduced GET-PUZZLE to GET_PUZZ,
but nobody ever introduced GET_PUZZ to PUZ.
What you were trying to do would only have succeeded if REBOL
used "dynamic scoping" such as Lisp or xBase use, in which new
variables are added to the global "environment" and are visible
to everyone after (chronologically!!!) the point of definition.
REBOL contexts don't behave that way.
Hope this helps!
-jn-
[7/16] from: ptretter:charter at: 18-Oct-2000 8:00
Once an object is made you can use "make (ancestor oject)!" on that new
object to create your new objects. At least if I remember correctly from
the RTOG.
Paul Tretter
[8/16] from: jelinem1:nationwide at: 18-Oct-2000 9:12
To create only one instance of the function, define the function first and
assign it to a word. Then, assign the object element to a reference of this
word. Example:
my-func: func [][print "hello!"]
obj1: make object! [f: :my-func]
obj2: make object! [f: :my-func]
same? (get in obj1 'f) (get in obj2 'f)
== true
- Michael Jelinek
[rishi--picostar--com] on 10/17/2000 04:44:00 PM
From: [rishi--picostar--com] on 10/17/2000 04:44 PM
Please respond to [list--rebol--com]
To: [list--rebol--com]
cc:
Subject: [REBOL] objects without overhead
I am working on a program which requires about 1000 or so instances of
objects. It doesn't have to be OO but I like the design better that way. I
am using a function as constructor like the following example (not real
code..):
;I used dashes to keep formatting intact (hopefully this works)
make_myobject: func[var1 var2 var3][
--return make object! [
----instancevar1: var1
----instancevar2: var2
----instancevar3: var3
----instancefunc1: func[][...]
----------------.
----------------.
----------------.
-]
]
The thing I don't like about this solution is that every instance of this
object has duplicate copies of the functions (As far as I know...please
correct me if I am wrong). This seems like a waste to me - especially if
the object has 50 or so functions. Is there any OO way in Rebol to create
multiple instances of this object without creating multiple copies of the
functions associated with each object? I know how to do this in a
non-object oriented fashion but would like to see an OO solution.
[9/16] from: ole_f:post3:tele:dk at: 18-Oct-2000 20:58
Hi , 17-Oct-2000 you wrote:
>The thing I don't like about this solution is that every instance of
>this object has duplicate copies of the functions
[...]
Just define a "parent" object containing all of your functions, then create
your "used" objects by cloning this object. The "used" objects then won't
include the functions, but as the "parent" object is the prototype for the
used
objects, then you can still use your functions through these objects.
Example (not tested):
parent: make object! [
func1: func [a] [print a]
func2: func [a b] [print [a b]]
]
child1: make parent [
var1: "This is a variable in a child object"
c-func1: func [a] [func1 join a var1]
]
child2: make parent [
var2: "This is another variable in another child object"
c-func2: func [a b] [func2 var2 join a b]
]
Well, I guess you get the idea.
Kind regards,
--
Ole
[10/16] from: rsnell:webtrends at: 19-Oct-2000 9:27
Ole,
I believe you are thinking the same way I did which is incorrect.
The functions are duplicated as evidenced by a response that Elan
sent about this very question a few weeks back. Here it is:
------- from [rebol--techscribe--com] ------
Hi Rodney,
I believe REBOL makes a new copy of functions for each derived object.
Let me introduce the players: A root object o, a function f, and a derived
object p:
>> o: make object! [f: func [] [print "a"] ]
>> p: make o []
At this point the two objects are look-alikes:
>> probe o
make object! [
f: func [][print "a"]
]
>> probe p
make object! [
f: func [][print "a"]
]
Let us replace the string "a" by the string "b" in p's function f
>> change at second get in p 'f 2 "b"
== []
Were we successful?
>> probe p
make object! [
f: func [][print "b"]
]
Yes. Was the function f in the parent object o also modified?
>> probe o
make object! [
f: func [][print "a"]
]
No!
Apparently modifications to the inherited function f in p do not propagate
to the f function in the parent object o, ergo the two functions are
independent of each other.
------- end ----------
Rodney
[11/16] from: rishi:picostar at: 20-Oct-2000 11:38
thanks for all the replies...
> Just define a "parent" object containing all of your functions, then create
> your "used" objects by cloning this object. The "used" objects then won't
<<quoted lines omitted: 14>>
> ]
> Well, I guess you get the idea.
hmmm... I could have sworn that cloned objects actually copy everything in the object
cloned. I guess I was wrong. Still, this doesn't solve my problem. The data in the cloned
object cannot be accessed from the functions defined in the parents object. Anyway, I
did find 2 solutions to my problem. The solution I chose was to break up my object into
one small one (that will be instantiated many times) and one big one that will manipulate
the many small objects. The other solution is to pass 'self to an outside funcion as
shown (pay attention to get-puzzle function):
;not tested..but I bet it works as is
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CODE BEGINS HERE
state: make object! [
new: func [
puzzle [block!]
][
return make object! [
puz: copy reduce puzzle
get-puzzle: :get_puzz self
]
]
get_puzz: func [obj] [return copy obj/puz]
]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;: TEST CODE
; comment {
test-obj: state/new [1 2 3 4 0 5 6 7 8]
print test-obj/get-puzzle
; }
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CODE ENDS HERE
Rishi
[12/16] from: ole_f:post3:tele:dk at: 22-Oct-2000 23:58
m>
Message-ID: <[22-Oct-0014381164ole_f--post3--tele--dk]>
MIME-Version: 1.0
X-Mailer: NewsWise ALPHA 0.3 by Leon Woestenberg & Ole Friis
Organization: ATO - Amiga Translators' Organization
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: quoted-printable
X-Wordwrap: 78
Hi Rodney, 19-Oct-2000 you wrote:
^^^^^^
Ah, at last: A list server which supports names in e-mail messages. That's a
feature I've really missed while the list has been running on Selma (hint)
;-)
>I believe you are thinking the same way I did which is incorrect.
>The functions are duplicated as evidenced by a response that Elan
>sent about this very question a few weeks back. Here it is:
(Sorry if I've missed some messages from time to time, but I just don't have
the time to read carefully through them all. So thanks for quoting Elan!!!)
OK, Elan _may_ be right, but look below.
[...]
>I believe REBOL makes a new copy of functions for each derived object.
[...]
>Let us replace the string "a" by the string "b" in p's function f
[...]
>Were we successful?
[...]
>Yes. Was the function f in the parent object o also modified?
[...]
>No!
>Apparently modifications to the inherited function f in p do not propagate
>to the f function in the parent object o, ergo the two functions are
>independent of each other.
So that's what the REBOL semantics apparently define. However, if the
_implementation_ of REBOL is clever, those two functions will refer to the
same function until you start modifying one of them. Then REBOL will split
them into two, and modify one of them.
This is, AFAIR, called "copy-on-write" and is also used in NewtonScript, the
script language that accompanied the Newton message pad from Apple. This
scripting language implementation had lots of available ROM, but not much
RAM, so "copy-on-write" saved lots of RAM this way (and used some ROM because
of the added complexity of the interpreter code). (BTW, NewtonScript is
prototype-based, just like REBOL.)
However, I wasn't aware that REBOL behaves the way that Elan describes, and
the above is only guesswork. Plus, the following REBOL session also indicates
that Elan may be totally right:
>> a: make object! [t: "aaa"]
>> b: make a []
>> c: make a []
>> b/t =? c/t
== false
In this session, I haven't altered a/t at all, so if the REBOL implementation
used simple "copy-on-write", the last expression should have evaluated to
true. Of course, it is possible to implement the above with "copy-on-write"
too, but I just have a feeling that REBOL Tech. haven't.
So, let's have a word from REBOL Tech.:
- Why was REBOL designed this way (as I don't see any benefits of doing it
that way, as I don't see memory overhead as a benefit)
- _Does_ the REBOL interpreter actually use "copy-on-write", or should we get
used to writing object-oriented REBOL programs in obscure ways to avoid
memory and speed penalties (the latter because the values in the prototype
object has to be copied somehow, and this takes time)?
The above two questions are intentionally written in a provocative way to,
well, provoke REBOL Tech. to answer them :-)
>Rodney
Kind regards,
--
Ole
[13/16] from: petr:krenzelok:trz:cz at: 23-Oct-2000 7:02
Ole Friis wrote:
> - Why was REBOL designed this way (as I don't see any benefits of doing it
> that way, as I don't see memory overhead as a benefit)
<<quoted lines omitted: 5>>
> The above two questions are intentionally written in a provocative way to,
> well, provoke REBOL Tech. to answer them :-)
... to add to the provocation ;-)))
- RT never answers such questions .... the just will tell you it's this way or
that way, but will not discuss why it's like it is ... :-)
- REBOL reference aproach is great, but should be noted in each doc on the first
lines in bold with several !!!! I remember the struggle with simple recursion
function, as well as shared sub-objects. Just try to have several progress bars
and change 'bar subobject of one of them - it will be reflected thru all of them
- unpleasant, unwanted behavior. So in the end you end up copying it yourself,
and ppl coming to your script wonder why you do things this way, and next time
another way.
btw: what about dialect for context creation? obj: make/spec some-obj []
[subobjects copy-on-write funcs share level 1 copy rest] blabla :-)
However, on the other hand - Carl's the designer and let's accept the policy he
surely knows why it's the way it is ... What's more - I hope adding module
support to /Core will make context issues much more clear, we just need to wait
for the addition.
Cheers,
-pekr-
[14/16] from: joel:neely:fedex at: 23-Oct-2000 8:56
Hi, Ole,
Your questions deal with an area of REBOL which I agree has been
VERY much "underdocumented"; I also would love to see a clear,
definitive statement from RT about what the semantics were
intended to be, and why.
While we're waiting for such a response from RT, let me offer
a couple of thoughts on my (current!) mental model of REBOL
objects which may help explain what's happening and why. (And,
as always, I'll be grateful for any responses that expose any
misconceptions that I may have...)
Ole Friis wrote:
[...snip...]
> > Apparently modifications to the inherited function f in p do not
> > propagate to the f function in the parent object o, ergo the two
<<quoted lines omitted: 10>>
> added complexity of the interpreter code). (BTW, NewtonScript is
> prototype-based, just like REBOL.)
Having used NewtonScript myself, I must interject that there are
some very significant differences between the two languages. I
would describe NewtonScript as using a "delegation model" for
objects -- Walter Smith has explicitly credited Self as being
the inspiration for this aspect of NewtonScript. In that model
there are no classes (as with REBOL), just objects.
THE NEWTONSCRIPT WAY...
UNLIKE REBOL, each object can refer to a "parent" object.
Any attempt to use a data member or method (note the distinction!)
of an object may fail to find the desired, named attribute. If
so, the search continues with the "parent" of the original object;
this process recurrs up the parent chain until either there are no
more parents (in which case there is an error) or the named element
is found. If the named attribute is a method, it is executed WITH
ALL VARIABLES SUBJECT TO THE SAME ATTRIBUTE LOOKUP AS BEFORE, BUT
BEGINNING WITH THE ORIGINAL OBJECT.
For example:
[I'm using pseudo-REBOL syntax here for the benefit of non-Newton-
Script folks on the list but this is NOT valid REBOL code -- nor
syntactically correct NewtonScript.]
grandma: make object! [
a: 1 b: 1 c: 1
sum: func [] [a + b + c]
]
mama: make delegating-object! [
_parent: grandma
b: 2
]
child: make delegating-object! [
_parent: mama
c: 3
]
grandma/sum == 3
mama/sum == 4
* child/sum == 6
child/a: 10
child/sum == 15
The evaluation of the starred expression is based on the text of
SUM, where C resolves to the C in CHILD, B resolves to the B in
MAMA, and A resolves to the A in GRANDMA. In the following line,
a new attribute is added to CHILD, which now "shadows" the A that
would have been "inherited" from the ancestry. Thus the last
expression evaluates using A in CHILD, B in MAMA, and C in CHILD.
Very dynamic, and very different from REBOL
THE REBOL WAY
The behavior of REBOL "objects" is different from any standard
model with which I am familiar. The difference arises from two
key facts about REBOL:
1) Evaluation of a REBOL word requires a "context" which gives
meaning to the word.
>> foo: make object! [
[ a: 1 b: 2 f: func [] [a + b]
[ g: func [b [block!]] [append b 'a]
[ ]
>> fum: make object! [
[ a: 4 b: 3 f: func [] [a - b]
[ g: func [b [block!]] [append b 'a]
[ ]
>> a: "Hello, "
== "Hello, "
>> b: "world!"
== "world!"
>> f: func [] [print join a b]
>> g: func [b [block!]] [append b 'a]
>> foo/f
== 3
>> fum/f
== 1
>> f
Hello, world!
>> c: []
== []
>> foo/g c
== [a]
>> fum/g c
== [a a]
>> g c
== [a a a]
>> reduce c
== [1 4 "Hello, "]
>> print c
1 4 Hello,
2) REBOL is a von Neumann Language -- it refuses to distinguish
between code and data (note again that this distinction is
made in NewtonScript).
>> h: to-paren [print ["eenie" "meenie" "mynie"]]
== (print ["eenie" "meenie" "mynie"])
>> h
eenie meenie mynie
>> append second :h "no mo!"
== ["eenie" "meenie" "mynie" "no mo!"]
>> h
eenie meenie mynie no mo!
>From the first point above, we see that it is impossible to know
how to evaluate a word (even given the spelling of its name!)
without knowing the context in which that word is being considered.
Further, word! values in REBOL (NOT the spellings of their names,
but the internal values actually involved) clearly refer back to
their defining contexts -- that's why the value of C only *appears*
to contain three mentions of the same word, when it actually
mentions three distinct words whose names are simply spelled alike!)
>From the second point above, we see that there's no way to know --
even in principle -- whether a REBOL value is code or data, because
to REBOL that is a distinction only of usage, not of nature.
With those two facts firmly in mind, now consider how to understand
what's happening in the following expansion of your example:
>> a: make object! [t: "aaa" speak: func [] [print t]]
>> b: make a []
>> c: make a []
>> same? b/t c/t
== false
Why are they not the same? In order for us to be able to do this:
>> b/t: "bbb"
== "bbb"
>> c/t: "ccc"
== "ccc"
>> a/speak
aaa
>> b/speak
bbb
>> c/speak
ccc
We need to be able to alter the value of a word within a specific
object and have subsequent evaluations in that object's context use
the new value -- without confusing it with another word in another
object EVEN IF THE NAMES ARE SPELLED THE SAME.
You might still be wondering, "But why not copy-on-write?" Well,
consider first the following:
>> t: "ttt"
== "ttt"
* >> a/speak: func [] [print t]
>> a/speak
ttt
>> a/t
== "aaa"
the T in the new value of A/SPEAK is no longer the same as the T
inside of A. At the point of creation of a function, each name
appearing in the body of the function must be converted to a word!
value, and (as we have already seen) each (internal) word! value
identifies its own context.
The starred line immediately above requires REBOL to create a
function body from the typed-in "source code", so REBOL "looks
up" the name {t} and finds only the global one, because I was
typing at the interpreter's command prompt. Therefore, THAT
is the T that is used in the function, and not the T from A.
[I consider my use of quotations in the preceding paragraph to
be necessary, as I am using non-standard terminology to
describe REBOL behavior that I've not been able to find described
in official REBOL documentation with official REBOL terminology.]
Therefore, when we said
>> b: make a []
earlier, REBOL had to make a context for B, then make a new T in
that context, then make a new SPEAK in that context. But in
making that new SPEAK ***the mention of {t} inside of the body
of SPEAK had to refer to the new T inside B and not the T of A.
If a copy-on-write strategy tried to make SPEAK of B refer to the
same function! value as that of SPEAK of A (only changing when SPEAK
of B is redefined), words inside B/SPEAK couldn't refer to the words
of B, but of A.
Incidentally, this also explains en passant the reason why function
bodies are distinct from the blocks which were used to create them
(i.e., why the function body is a distinct copy of the block):
>> body: [print "Hello, world!"]
== [print "Hello, world!"]
>> sayhi: func [] body
>> sayhi
Hello, world!
>> change next body "Hi, mom!"
== []
>> body
== [print "Hi, mom!"]
>> sayhi
Hello, world!
>> source sayhi
sayhi: func [][print "Hello, world!"]
Changing BODY no longer changes the behavior of SAYHI because words
used in SAYHI must be understood in the context in which SAYHI was
defined. The fastest way to do this is to construct a new body
for SAYHI, using the block supplied to FUNC as "inspiration" rather
than as a value to which the new function can simply refer.
As one final, more extreme set of examples, consider:
>> body: [print [a + b + c]]
== [print [a + b + c]]
>> a: b: c: 1
== 1
>> f: func [] body
>> f
3
>> obj0: make object! [
[ a: b: c: 2
[ f: func [] body
[ ]
>> obj0/f
3
The names inside BODY already had been translated to internal word!
values, which were defined in the global context, therefore those
were the words used inside OBJ0/F (a surprise!)
>> bodystring: "print [a + b + c]"
== "print [a + b + c]"
>> obj1: make object! [
[ a: b: c: 3
[ f: func [] to-block bodystring
[ ]
>> obj1/f
** Script Error: print is not defined in this context.
** Where: print [a + b + c]
>> probe obj1
make object! [
a: 3
b: 3
c: 3
f: func [][print [a + b + c]]
]
Obviously (?) the process of converting names to word! values is
not defined (or currently implemented, at least!) to expand its
lookup outside the context immediate at hand. Thus, the name
{print} was *assumed* to refer to a word! in the context of
OBJ1 -- presumably with the intent of allowing forward references,
but that's just speculation on my part -- and was not understood
as mention of the global PRINT function.
Many more such obscure cases can be constructed. Absent any RT
documentation of intent, it is impossible to determine whether
any of these are bugs.
> - Why was REBOL designed this way (as I don't see any benefits of
> doing it that way, as I don't see memory overhead as a benefit)
>
Again, absent of any RT documentation, it is impossible to tell
whether this is
1) Intentional, according to some undocumented goals for how
objects should behave;
2) A logically-unavoidable consequence of some undocumented goals
for how functions, contexts, and words should behave;
3) An unplanned consequence of how functions, contexts, and words
are currently implemented (i.e., could have been differently
done, with subtly different behavior); or
4) Some combination of the above.
The most troubling implication of #3 is that a future version of
REBOL might have to make a choice between perpetuating legacy
behavior even in the face of a need to change, or breaking user-
written code because an implementation detail does change.
> - _Does_ the REBOL interpreter actually use "copy-on-write",
>
No, AFAICT.
> or should we get used to writing object-oriented REBOL programs
> in obscure ways to avoid memory and speed penalties (the latter
> because the values in the prototype object has to be copied
> somehow, and this takes time)?
>
I hope this little essay has provided at least a conceptual model
for REBOL objects. I certainly don't claim that it explains the
actual thought processes that were involved in the design and
evolution of REBOL.
> The above two questions are intentionally written in a
> provocative way to, well, provoke REBOL Tech. to answer them :-)
>
As were my comments in this little essay.
However, for about a year I've been writing my attempts at
conceptual models for REBOL behavior (as much to clarify my own
thinking, as to try to help fellow REBOL programmers).
To the best of my knowledge, none of them have provoked a reply from
RT stating either "you got it right" or "you got it wrong and here's
what you should have said" or even "you got it wrong."
Hey, RT, please understand that I'm not trying to offend anyone, nor
cast unfair criticism! What I *am* trying to do is understand REBOL
and help explain it to myself and my fellow programmers. That is
(in part) my way of trying to help REBOL (and RT) succeed. I hope
that you'll take my comments in that spirit, and confirm or correct
my attempts at explanation in like spirit.
-jn-
[15/16] from: lmecir:geocities at: 24-Oct-2000 13:21
Hi Rishi,
as an inspiration, I am sending you my old class.r
Rebol [
Title: "Class"
File: %class.r
Author: "Ladislav Mecir"
Email: [lmecir--geocities--com]
Purpose: {
A class hierarchy in Rebol
}
Category: [Advanced]
]
throw-on-error: func [[throw] blk [block!] /local result] [
if error? set/any 'blk try blk [
throw :blk
]
get/any 'blk
]
; method creator
meth: func [
{defines a user method with given spec and body}
[catch]
spec [block!]
body [block!]
/local result
] [
; try the consistence
throw-on-error [
func spec copy/deep body
]
func [self] reduce [
:func :spec 'bind/copy :body :in 'self to lit-word! 'self
]
]
; method calling dialect:
!: func [
{method caller}
[catch]
message [block!] {
The structure of a message is as follows:
method-path
object
additional arguments according to the method spec
}
/local method-path method-name self position cls result
] [
method-path: throw-on-error [first :message]
if not path? get/any 'method-path [
method-path: throw-on-error [
to path! get/any 'method-path
]
]
method-name: first :method-path
position: next message
result: throw-on-error [do/next position]
set/any [self position] :result
method-name: throw-on-error [
in get get in :self 'class method-name
]
if not method-name [
throw make error! {no such method}
]
method-name: get method-name
method-name: method-name self
change :method-path 'method-name
do compose [(:method-path) (position)]
]
; sample class
sample-class: make object! [
; a method returning the object type
type: meth [] [
class
]
; a static value - counter
counter: 0
; a counting method
count: meth [] [
counter: counter + 1
]
reftest: meth [/ref] [
either ref [
print "Ref"
] [
print "No ref"
]
]
num: meth [] [number]
addnum: meth [other] [(! [num other]) + (! [num self])]
]
; sample objects of a sample-class
sample-object1: make object! [
class: 'sample-class
number: ! [count self]
]
probe sample-object1
sample-object2: make sample-object1 [
number: ! [count self]
]
probe sample-object2
; sample method calls
probe ! [type sample-object1]
probe ! [type sample-object2]
! [reftest sample-object2]
! [reftest/ref sample-object2]
probe ! [num sample-object1]
probe ! [num sample-object2]
probe ! [addnum sample-object1 sample-object2]
probe ! [addnum sample-object2 sample-object1]
Regards
Ladislav
> Well...I thought about this a bit and I THINK I found an elegant solution.
So I guess I'll respond to my own question...
> I think the solution to this problem is inner objects. Here is an example:
> myobject: make object! [
<<quoted lines omitted: 16>>
> coolobj: myobject/make
> coolobj/instancfunc1 ; all instances access same function! less overhead
if many objects!!!
> .
> .
> .
> I am not sure if this would work since I have never tested it (yet). The
questions in my mind are:
> 1. are functions copied by reference or value? if by value, this would not
solve my problem
> 2. do inner objects have access to outer object words? for example, can
'instancefunc1 access 'function1 as shown above?
> I hope this works. If it does, it would be a very simple but elegant
solution to my problem. It would be even more elegent when modules come
around so I can keep all private words in context of myobject rather than
the actual object returned...making code very clear.
> Rishi
>
> Previously, you ([rishi--picostar--com]) wrote:
> > I am working on a program which requires about 1000 or so instances of
objects. It doesn't have to be OO but I like the design better that way. I
am using a function as constructor like the following example (not real
code..):
> >
> > ;I used dashes to keep formatting intact (hopefully this works)
<<quoted lines omitted: 14>>
> >
> > The thing I don't like about this solution is that every instance of
this object has duplicate copies of the functions (As far as I know...please
correct me if I am wrong). This seems like a waste to me - especially if the
object has 50 or so functions. Is there any OO way in Rebol to create
multiple instances of this object without creating multiple copies of the
functions associated with each object? I know how to do this in a non-object
oriented fashion but would like to see an OO solution.
[16/16] from: g:santilli:tiscalinet:it at: 24-Oct-2000 13:28
Ole Friis wrote:
> So that's what the REBOL semantics apparently define. However, if the
> _implementation_ of REBOL is clever, those two functions will refer to the
> same function until you start modifying one of them. Then REBOL will split
> them into two, and modify one of them.
It can't work this way, because the function code NEEDS to be
copied and modified each time a new object is created; this is
because you need to bind it to the new object, and keep the old
one bound to the old.
> - Why was REBOL designed this way (as I don't see any benefits of doing it
> that way, as I don't see memory overhead as a benefit)
See above.
> - _Does_ the REBOL interpreter actually use "copy-on-write", or should we
> get
> used to writing object-oriented REBOL programs in obscure ways to avoid
> memory and speed penalties (the latter because the values in the prototype
> object has to be copied somehow, and this takes time)?
You can simply use delegation. Or you can throw OOP away, and use
a simpler approach. :-)
HTH,
Gabriele.
--
Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer
Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/
Notes
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted