Mailing List Archive: 49091 messages
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

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