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

More on "embedded" objects (an enhancement)

 [1/1] from: joel::neely::fedex::com at: 16-May-2001 8:06

Hello, self and list, ;-) As promised from last night... (The coding was trivial, but I wanted time to run a bunch of test cases and write a better description before posting.) Joel Neely wrote:
> A possible future enhancement (which it's too late for me > to tackle tonight) would be to add another refinement to > allow sharable attribute creation when instantiating a new > object from an existing object. I may take a quick look at > that in the morning. >
The enhanced version with a /SHARED refinement appears below, followed by new and improved (I hope) documentation. 8<---------------------------------------------------------- instantiate: func [ [catch] spec1 [block! object!] /unique spec2 [block!] /shared spec3 [block!] /local ospec uspec uattr result ][ ospec: copy [] uspec: copy [] uattr: to-word "; unique" if all [object? spec1 found? in spec1 uattr] [ append uspec get in spec1 uattr ] if unique [ append uspec spec2 ] if 0 < length? ospec: copy/deep uspec [ append ospec reduce [to-set-word uattr 0] ] if shared [ append ospec spec3 ] result: either object? spec1 [ make spec1 ospec ][ make object! append copy/deep spec1 ospec ] if 0 < length? uspec [ set in result uattr uspec ] result ] 8<---------------------------------------------------------- INSTANTIATE constructs objects. In the simplest case (i.e. no refinements -- ever!) it is almost the same as MAKE OBJECT! with the following exceptions: new-object: instantiate spec-block creates an object from the supplied block, but words in that block are NOT bound to the new objects context. In addition new-object: instantiate spec-object creates a new object from the supplied object, without the need for an empty block when no modifications are desired. The /UNIQUE refinement requires an additional block, which is combined with the original spec (block or object) to override and/or extend attributes for the new object, just as new-object: make spec-object extra-block can incorporate changes/additions to attributes. However, there's another difference. When creating an object via new-object: instantiate/unique spec-thing u-block (where SPEC-THING can be either a block or another object) the new object will distinguish between its "shared" parts and its "unique" parts. If yet another object is created later using newer-object: instantiate new-object the *current* state of the shared parts will be used as the basis of the newer object, but the *original* unique spec will be used to create a fresh copy of all unique parts. To illustrate: o1: instantiate/unique [ a: 1 ][ b: 10 gimme: func [] [print ["I contain" a "and" b]] ] creates an object with a shared A and unique B, with the following behavior
>> o1/gimme
I contain 1 and 10
>> o1/a: 2
== 2
>> o1/b: 20
== 20
>> o1/gimme
I contain 2 and 20 Using O1 as the basis for a later object give the following results,
>> o2: instantiate o1 >> o2/gimme
I contain 2 and 10 where the unique attribute O2/B is initialized per the original unique spec block, rather than coming from the current state in O1. The /SHARED refinement allows an additional spec block to be supplied to override/extend the original specification, but *without* marking its content as unique. Therefore,
>> o3: instantiate/shared o2 [a: 100] >> o3/gimme
I contain 100 and 10 overrides the current O2/A, but that change is not marked as unique. As the following transcript shows
>> o3/a: 200
== 200
>> o3/b: 30
== 30
>> o3/gimme
I contain 200 and 30
>> o4: instantiate o3 >> o4/gimme
I contain 200 and 10 the A attribute still comes from the current spec object, while future B attributes still revert to the original specification. The /SHARED refinement is allowed when building from a spec block as well. It will extend/override the base spec block, which creates "shared" attributes by default. Finally, both the /UNIQUE and /SHARED refinements are cumulative in effect:
>> o: instantiate/unique [
[ a: 1 [ ][ [ b: 10 [ sum1: func [][a + b] [ ]
>> o/a: 2
== 2
>> o/b: 20
== 20
>> o2: instantiate/shared o [
[ c: 100 [ sum2: func [][a + b + c] [ ]
>> o3: instantiate/unique o2 [
[ d: 1000 [ sum3: func [][a + b + c + d] [ ] so that A and C are shared, while B and D are unique
>> o3/a: 3
== 3
>> o3/b: 30
== 30
>> o3/c: 300
== 300
>> o3/d: 3000
== 3000
>> o4: instantiate o3 >> o4/sum3
== 1313 Finally, of course (at last!), the most subtle and critical issue with /SHARED (or default) versus /UNIQUE is this: since the cumulative unique specifications in a prototype object are re-evaluated from a clean copy whenever that prototype object is INSTANTIATE-d, 1) any UNIQUE initialization activity will be re-evaluated in the combined context of the new object and the global context, and 2) any UNIQUE-ly "embedded" sub-objects will be re-created in the new context (1), while SHARED "embedded" sub- objects will simply refer back to the sub-object in the prototype object. Enclosing-object words in an embedded sub-object will refer to the original enclosure for SHARED (or default) sub-objects but will refer to the current enclosure for UNIQUE sub-objects. Hope this helps! (And if you read this far, I owe you a virtual cookie for your patience and endurance!) -jn- -- ------------------------------------------------------------ Programming languages: compact, powerful, simple ... Pick any two! joel'dot'neely'at'fedex'dot'com