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

[REBOL] Re: Obscure? You be the judge!

From: joel:neely:fedex at: 15-May-2002 8:32

Hello, all, (I'm replying to self because several folks have made helpful comments during this thread. Thanks to all who've contributed!) I've done further experimenting to try to clarify the differences for myself. Perhaps they will be of help to someone else. My apologies for the length, but I'd appreciate comments on the coding convention at the end. -jn- Joel Neely wrote:
> Thanks to your explanation, I have a more compact example... >
Thanks to comments from Volker, Gabriele, and Ladislav, I have a more complete example, given below. I'm sure that all I've done here is re-discover/-state what was already said, but I'm a bit slow... ;-) gub?: make object! [ data: [1] proto: make object! [ number: 0 dataref: data speak: func [] [print [number mold dataref]] ] things: [] append things make proto [number: 1] append things make proto [number: 2 dataref: data] append things make proto [number: 3 dataref: proto/dataref] tweak-n-speak: func [blk [block!]] [ insert data blk proto/speak foreach thing things [thing/speak] ] ] By adding the line containing "PROTO/SPEAK" to the last function, I was able to get more photons from the filament!
>> gub?/tweak-n-speak ["what's" "different?"]
0 ["what's" "different?" 1] 1 [1] 2 ["what's" "different?" 1] 3 ["what's" "different?" 1] When GUB?/PROTO was made, its DATAREF was initialized to refer to GUB?/DATA as expected. When GUB?/PROTO/1 was made, it began life as a (deep) clone of GUB?/PROTO and then had its NUMBER set to 1. Its DATAREF now refers to a clone of the initial GUB?/DATA (via the reference in GUB?/PROTO/DATAREF). When GUB?/PROTO/2 was made, it began life as a (deep) clone of GUB?/PROTO and then had its NUMBER set to 2, *and* its DATAREF (re-)set to refer to the same series as GUB?/DATA (explicitly, via the GUB?/DATA reference). When GUB?/PROTO/3 was made, it began life as a (deep) clone of GUB?/PROTO and then had its NUMBER set to 3, *and* its DATAREF (re-)set to refer to the same series as GUB?/DATA (explicitly, via the GUB?/PROTO/DATAREF reference). At that point the references in GUB?/DATA and GUB?/PROTO/DATAREF and GUB?/THINGS/2/DATAREF and GUB?/THINGS/3/DATAREF are all equivalent, while the reference in GUB?/THINGS/1/DATAREF refers to a distinct block (originally cloned via GUB?/PROTO/DATAREF). Normal shared-block-reference behavior ensues *after* this point. KEY LESSON: When using an existing object as a prototype, the default behavior is to copy referenced *values*, and special effort must be taken to *suppress* the copying, in contrast to other REBOL handling of reference data, where the default behavior is to copy *references* and special effort must be taken to *cause* copying. For example, every REBOL programmer eventually learns that: palindrome: func [b [block!] /local result] [ result: [] append result b append result head reverse b result ] will (probably) surprise him/her *twice*, as in:
>> foo: [1 3 5 7]
== [1 3 5 7]
>> baz: palindrome foo
== [1 3 5 7 7 5 3 1]
>> foo
== [7 5 3 1]
>> bletch: palindrome [2 4 6]
== [1 3 5 7 7 5 3 1 2 4 6 6 4 2] and therefore should (probably) be written more like: safe-palindrome: func [b [block!] /local result] [ result: copy [] append result b append result head reverse copy b result ] (Yes, I know this dinky example can be written *much* better; please don't miss the point and try to optimize this specimen of noncopy-vs-noncopy behavior. If I had rewritten it as safe-and-short-palindrome: func [b [block!]] [ join b head reverse copy b ] the point regarding copying would have been harder to see.) In contrast to the you-must-ask-for-copying-if-you-want-it general rule, when creating objects from a prototype object that is intended to contain reference(s) to shared block(s), REBOL requires that we take special action to *avoid* the default copying behavior. It seems to me that the clearest way to do this is likely something similar to what I did above with the third anonymous object in GUB?/THINGS where the prototype contains: proto: make object! [ number: 0 dataref: data ;;... ] and the construction-from-prototype pattern of make proto [ number: 3 dataref: proto/dataref ] explicitly calls the reader's attention to the fact that the DATAREF attribute of the new object is intended to share reference with the corresponsing attribute of the prototype. (In passing, it's interesting that NUMBER must be initialized explicitly to get a different value from the prototype, but DATAREF must be initialized explicitly to get the same value as the protype.) I'm leaning toward writing dataref: proto/dataref instead of dataref: data in the construction-from-prototype expression to make it clear that the *current* value of the prototype's attribute should be the basis of the shared reference. For example... some-proto: make object! [ block-ref: some-global-block ;;... ] other-proto: make first-proto [ block-ref: other-global-block ;;... ] leaves a situation in which some-instance: make some-proto [ block-ref: some-proto/block-ref ;;... ] other-instance: make other-proto [ block-ref: other-proto/block-ref ;;... ] is more likely correct (and obvious to the reader) than some-instance: make some-proto [ block-ref: some-global-block ;;... ] other-instance: make other-proto [ block-ref: some-global-block ;;... ] Thoughts? -- ; Joel Neely joeldotneelyatfedexdotcom REBOL [] do [ do func [s] [ foreach [a b] s [prin b] ] sort/skip do function [s] [t] [ t: "" foreach [a b] s [repend t [b a]] t ] { | e s m!zauafBpcvekexEohthjJakwLrngohOqrlryRnsctdtiub} 2 ]