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

Words, Bindings and Contexts. (1)

 [1/17] from: lmecir::geocities::com at: 23-Jul-2000 19:01


Hi Rebols, Mark Dickson wrote: 1. What contexts are? 2. Why are they important? 3. How are they determined i.e global or local ? 4. How, where & when are "new" context created ? 5. What is the lifespan of new context? 6. Why is it useful to know about this stuff ? 7. What productive use can be made of this ? Here are my answers: (feel free to suggest a better terminology or wording) What are Rebol Words? **************************** Word! is a very important Rebol datatype. A word can be used as a value eg.: probe 'a , or: equal? 'a 'b etc. What are Rebol Bindings? ****************************** The most important property of Rebol Words is the ability to contain any Rebol Values, so you can "store" a Rebol Value in a Word like: a: 12 set 'a 12 set/any 'a 12 and get the "stored" value back as: :a get 'a get/any 'a
>From the printable form of a Word like mold 'a, mold 'b, etc., you
cannot decide, if and what value is stored in it. Even equal words can store different values, as can be seen here: ; create a block Blk containing a word 'a blk: copy [a] a: 12 ; now append another word to Blk b: make object! [append blk 'a a: 13] probe blk ; test if blk contains equal words equal? first blk second blk equal? get first blk get second blk What is the reason behind such a "mystery"? The answer is simple: *Words have Bindings* and the first Word in Blk has a different Binding, than the second. The former sentence looks logical, but it doesn't make a good sense yet, because I didn't say, what are Rebol Bindings. Let's put a sense into it: *Rebol Binding definition:* Every Rebol Word has attributes allowing it to "contain" Rebol Values. The set of that attributes I am calling the Word's Binding. When do Words have equal Bindings? A few observations: a) different (ie. Not-equal) Words have different Bindings b) equal words may have either equal or different Bindings (see above) c) the same Words have equal Bindings d) Words, that have equal Bindings are the same Illustration of d): same? first blk (bind second blk first blk) to be continued...

 [2/17] from: lmecir:geocities at: 23-Jul-2000 20:47


Rebol Contexts ****************** Before introducing other properties of Bindings, I will try to introduce Contexts. Rebol Contexts are existing data, so we are not free to use any definition of them. The "proof": some Contexts are directly available: Global Context is available as Rebol/Words Objects Some Contexts are not directly available, but because there is a GC fault, they can be garbage-collected, which proves, that they really existed (I cannot imagine collection of a non-existent thing). Here are some functions enabling us to access contexts not directly available: global: func [ {create a Global Context Word equal to the given Word} word [word!] ] [ ; this is just one of many possibilities, how to do it make word word ] global?: func [ {find out, if the given Word is a Global Context Word} word [word!] ] [ same? word global word ] ; this function can help us explore the Contexts, that aren't directly available same-context?: func [ {find out, if the given Words are the Words of the same Context} word1 [word!] word2 [word!] /local word3 ] [ if all [global? word1 global? word2] [return true] if any [global? word1 global? word2] [return false] if global? word3: bind global word1 word2 [return false] same? word1 word3 ] to be continued...

 [3/17] from: lmecir:geocities at: 23-Jul-2000 23:24


Allow me to present a cleaned Same-context? function. Sorry for any inconvenience due to reading the former - more complicated implementation. ; this function can help us explore the Contexts, that aren't directly available same-context?: func [ {find out, if the given Words are the Words of the same Context} word1 [word!] word2 [word!] ] [ either global? word1 [ global? word2 ] [ same? word1 bind global word1 word2 ] ] Context hierarchy. Pardon? ******************************** Example: f: func [f-arg] [ prin "F-arg: " print f-arg g: func [g-arg] [ prin "G-arg: " print g-arg ] g "This is g's argument." ] f "This is f's argument" If there were any context hierarchy, then the F's Context should have been a part of G's Context, which means, that F-arg should have been in G's Context together with G-arg. Let's see: word1: probe fourth second :f word2: probe fourth second get pick second :f 9 same-context? word1 word2 == false Context hierarchy ? Aye, aye, sir! **************************************** f: func [x] [ g "this is g's argument." print x ] g: func [y] [ x: y ] word1: second second :g bind second :g fourth second :f bind second :g word1
>> f "this is f's argument"
this is g's argument. Does this prove any "Context Hierarchy?" (the answer left to the reader) to be continued...

 [4/17] from: lmecir:geocities at: 24-Jul-2000 2:20


Note: The last two sections (Context Hierarchy. Pardon?; "Context hierarchy"? Aye, aye, sir!) were complicated and not very useful, but they can be skipped. What are Rebol Contexts? ***************************** Rebol Contexts are existing data, sometimes available to the user. They are used internally by Rebol for the purposes of many Core functions. Their main purpose is to serve as attribute of Rebol Words - ie. Rebol Context is a part of any Word's Binding - in different wording: every Rebol Word knows its Context. Proof: see the function Same-context? defined in part 3. Known Rebol Contexts: Available directly as: a) Global Context Rebol/Words b) Objects themselves c) Function Contexts not available d) Use Contexts not available e) Special Contexts not available Properties: a) Global Context is the Default Context - Words are normally Global Context Words, if not stated otherwise. b) Objects are Local Contexts - as opposed to Global Context. They are created by Make native. The list of Words object O contains - ie. the list of Words that are local to O is available as First O. But look out! There is a catch - the elements of First O are equal to local O's Words, but the elements of First O are Special Context Words. To obtain a list of Words really local to O, one must do: words-local-to-o: bind first o in o 'self c) Function Contexts are Local Contexts too. They are attributes of functions and are created when the respective functions are created. Function Contexts contain: all argument Words, all refinement Words, all optional argument Words and all local Words specified by /local refinement. (you can verify it for particular examples by using Same-context? function). d) Use Contexts are Local Contexts and are created when the respective Use function is evaluated. They contain the Local Words specified by the first argument of Use. e) Special Contexts - see a note under Object Contexts, other example: a: "q" a: make block! a get first a Here First A is a Special Context Word too. The problem is, that Bind doesn't work for Special Contexts, which means, that our Same-context? function doesn't work too. Here is an improved version: special-context?: func [ {determines, if a word is a Special Context Word} word [word!] ] [ error? try [error? get/any word] ] ; this function can help us explore the Contexts, that aren't directly available same-context?: func [ {find out, if the given Words are the Words of the same Context} word1 [word!] word2 [word!] ] [ either global? word1 [ global? word2 ] [ either special-context? word2 [ special-context? word1 ] [ same? word1 bind global word1 word2 ] ] ]

 [5/17] from: lmecir:geocities at: 24-Jul-2000 15:09


In the part 4 I forgot to introduce a special kind of Contexts: f) Dangerous Contexts. Example: f: func [x] [] special-context? first first :f CRASH I obviously cannot claim that Same-context? works in this case. Now I supply another interesting function: ; actually, Thomas Jensen was first there (nice work), but this function is able to find even the Unset Words context-words: func [ {returns all Context Words of a Word's Context} word [word!] /local result c-word ] [ either special-context? word [ ; we are out of luck in this case none ] [ result: copy [] foreach g-word first system/words [ if not same? g-word c-word: bind g-word word [ append result c-word ] ] result ] ] to be continued...

 [6/17] from: jeff:rebol at: 24-Jul-2000 9:00


I've always thought that someone could use REBOL/view to create an animated colored map presentation which demonstrates the nature of contexts... -jeff

 [7/17] from: lmecir:geocities at: 24-Jul-2000 19:55


In the previous sections I answered some questions regarding Contexts. The lifetime Contexts: ************************* The Global Context should last forever, Local Context have indefinite extent, which should mean, that if there were no GC bugs, they should last as long, as they are accessible. Now something on Bindings: Bindings revealed: ********************** I think, that I succeeded to demonstrate, that every Rebol Word has got a Context attribute. The next attribute everybody is familiar with, is its Mold attribute. It is simply the value that can be obtained by: mold 'word The Context and Mold attributes are normally enough to characterize a Rebol Word. A special case can be discussed: Aliases. As a toy I present a function able to find out, if two given words are aliases of each other: aliases?: func [ {find out, if word1 and word2 are aliases} word1 [word!] word2 [word!] ] [ all [ ; aliases are equal in Rebol equal? word1 word2 ; but have different Mold attributes not equal? mold word1 mold word2 ] ] Aliases are not currently considered by Rebol as the same Words. (Because their Mold attributes differ?) Observation concerning Rebol Words is as follows: Two Words are the same, if their Context attributes are the same and their Mold attributes are equal. (This would be true probably for all Contexts, but we haven't got the means to check the case of Special Contexts and Dangerous Contexts.) Example illustrating, that the same value and equality of Words doesn't mean the same Binding: blk: copy [a] a: 11 b: make object! [append blk 'a a: 11] print [ mold blk ":" mold reduce blk ] print [ "Do the first and the second element above have the same Binding?" same-context? first blk second blk ] What I didn't discuss, is the case of recursive functions in Rebol, but I hope, that somebody finds even the present text helpful. (If any of the questions at the beginning remains unclear, feel free to ask.) Ladislav

 [8/17] from: giesse:writeme at: 24-Jul-2000 20:17


Hey, I did get only Jeff's message from this thread! Could someone at RT check if they get errors from my account? (Don't need to send me the thread, I'll have a look at it in the archive. In the meantime, I'm sorry if someone asked something to me and I did not answer yet.) Regards, Gabriele. -- Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/

 [9/17] from: agem:crosswinds at: 24-Jul-2000 19:39


[ REBOL[ version: 1.0.0.5 file: %/home/volker/edis/screbol-0.1.6.4/public/test.r date: 24-Jul-2000/19:33:50+1:00 author: "volker" purpose: { here is a context-simulator to show what happens with binding. AFAIK. in a way Elan is right with inheritance, virtual function tables in c++ are created similar. on the other hand he is "wrong", because scoping in rebol can be a bit confusing, as Gabriele showed with objects. but inheritance with objects is made by 'make in another way, I think one should think about binding by replacing same-named addresses from another context with functions bindings are summed, and each new call to 'func binds its body to its argument-list. this replaces all bindings to global names, if this names are local variables. so getting "scoping". for parameters i think Gabrieles push/pop arg-list-values are right. in Gabrieles nested-object sample for scoping he should write [make make make object! [][][] ] to get scoping, in his sample all stuff is bound to global, then 'object! . it can't be bound to the static scope (=brackets around it in sourcecode) because at block-creation no sub-context exists..
>> obj1: make object! [
[ a: 2 [ obj2: make object! [ [ b: 2 .. Volker } ] {we nedd a memory-simulation} memory: array 10 set-m: func [n][pick memory n] get-m: func [n v][poke memory n v] top: 1 new-adr: func [/local res][res: top top: add top 1] is-unset: 0 {and one for contexts} ;note the need for copy contents! use make-context! context: [words: make hash! [] addresses: make block! []] new-context!: func [][make object! context] get-adr-for: func [context word /local there adr][ there: find context/words word either there [adr: pick context/addresses index? there] [none] ] set-c: func [context word value /local adr][ adr: get-adr-for context word if not adr [ adr: new-adr append context/words word append context/addresses adr ] poke memory adr value ] get-c: func [context word /local][ pick memory get-adr-for context word ] {to simulate and dump code-blocks this} code-entry!: make object! [ context: none word: none adr: none ] make-code-entry: func [.word /local id .adr res][ make code-entry! [context: 'no-context-yet word: .word adr: is-unset] ] {and a bind-simulation (not for subblocks yet)} bind-c: func [.context code /local adr][ foreach entry code [ adr: get-adr-for .context entry/word if adr [entry/adr: adr entry/context: .context] ] ] print {^/^/now the testing} print {^/--- some data^/} a-context: new-context! another-context: new-context! some-code: reduce [ make-code-entry 'a-word make-code-entry 'b-word make-code-entry 'c-word ] ? a-context ? another-context print "" ? some-code ? memory print {^/--- filling a bit in^/} set-c a-context 'a-word "a-word" set-c another-context 'b-word "b-word" set-c a-context 'c-word "c-word-in a-context" set-c another-context 'c-word "c-word-in another-context" ? a-context ? another-context print "" ? some-code ? memory print {^/--- binding first to a-context^/} bind-c a-context some-code ? some-code print {^/--- binding then to another-context^/} bind-c another-context some-code ? some-code print {^/in "original" the binding is performed to all sub-blocks} ]

 [10/17] from: lmecir:geocities at: 25-Jul-2000 22:37


I see, that the fact, that my series didn't explain the behaviour of functions WRT Recursion and Binding is a flaw. Here is the continuation (a model of the behaviour): ; Model of Rebol function: ; **************************** sim-function!: make object! [ ; every function has got a Context attribute context: none ; every function has got a Spec attribute spec: none ; every function has got a Body attribute body: none ; every function has got a provision for Recursion recursion-level: 0 stack: none ] ; Model of Func function: ; *************************** sim-func: func [ {create a Sim-function!} spec [block!] body [block!] /local context-init spec-too body-too ] [ ; first of all, the function context should be created context-init: make block! 1 + length? spec foreach word spec [ append context-init to set-word! word ] append context-init none spec-too: spec body-too: body make sim-function! [ ; create context context: make object! context-init ; a simplification here spec: bind/copy spec-too in context 'self ; this is the secret of "Context Hierachy" body: bind/copy body-too in context 'self ; Create a stack for storing values during recursive calls stack: copy [] ] ] ; Model of the function body execution: ; ****************************************** do-body: func [body] [do body] ; Model of the return from function: ; ************************************** sim-return: func [ sim-f value [any-type!] ] [ ; restore the former values from stack, if needed if (sim-f/recursion-level: sim-f/recursion-level - 1) > 0 [ set/any sim-f/spec first sim-f/stack ; finish the stack-pop sim-f/stack: remove sim-f/stack ] ; return the value return get/any 'value ] ; Model of function Evaluation: ; ********************************* sim-evaluate: func [ {evaluate a sim-function contained in a block with its arguments} block [block!] /local sim-f actual-values ] [ ; evaluate the arguments block: reduce block ; the executed sim-function is first sim-f: first block ; detect recursion if (sim-f/recursion-level: sim-f/recursion-level + 1) > 1 [ ; get the actual values of local words actual-values: copy [] foreach word sim-f/spec [ append/only actual-values get/any word ] ; push the actual values to stack sim-f/stack: head insert/only sim-f/stack actual-values ] ; give local words the supplied values set/any sim-f/spec next block ; execute the function body and return the result return sim-return sim-f do-body sim-f/body ] ; Some tests: ; ************** blk: copy [] probeblk: func [] [ prin mold blk prin ": " print mold reduce blk ] recfun: sim-func [x] [ append blk 'x either x <= 1 [ probeblk ] [ sim-evaluate [recfun x - 1] ] ] sim-evaluate [recfun 3] probeblk

 [11/17] from: giesse:writeme at: 26-Jul-2000 18:31


Hello [lmecir--geocities--com]! On 25-Lug-00, you wrote: l> I see, that the fact, that my series didn't explain the l> behaviour of functions WRT Recursion and Binding is a flaw. l> Here is the continuation (a model of the behaviour): [...] I just want to say that I agree with this model. This is the closest representation of REBOL's function context handling IMHO. OTOH, I'd like to understand why Carl considers the current behaviour a bug, while I was convinced this was by design. Still losing messages, Gabriele. -- Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/

 [12/17] from: larry:ecotope at: 26-Jul-2000 11:33


HI Ladislav Very cool! Use of simulated behavior (implemented in REBOL) is a concise and precise way of expressing one's thoughts about the workings of REBOL functions. I find it much more informative than lengthy attempts to describe in ordinary language how functions work. I have followed your Words, Bindings, and Contexts posts with great interest. I have learned a lot from your discussions and hope to see more. Just a couple of quick questions: Can you say more about Dangerous Contexts? For instance:
>> f: func [x][return x] >> first :f
== [x]
>> type? first first :f
== word!
>>value? first first :f ;REBOL crashes on any attempt to examine the
words in first :f Isn't this just a bug in REBOL? Why does it not return an error instead of crash? How about a modifying to sim-func to allow handling of /local and refinements? Then it would be a more complete model of func. Thank you again for sharing this excellent material. -Larry

 [13/17] from: lmecir:geocities at: 29-Jul-2000 9:56


The last part was about the behaviour of Rebol Functions. The present behaviour can be called "Computed Quasi Static Binding with Dynamic Recursion Patch." Interesting about the "Computed Quasi Static Binding" is, that it can handle recursion even without the "Dynamic Recursion Patch", as can be seen in the next model: ; Model of Context creation: ; ************************** make-context: func [ {create a Context containing given Words} words [block!] /local context-init result ] [ ; this implementation has got an issue: ; the created Context always contains 'Self words: difference/only words [self] context-init: make block! 4 * length? words foreach word words [ append context-init to set-word! word append context-init none append context-init :unset append context-init to lit-word! word ] result: make object! context-init unset in result 'self result ] { The following version of Same-context? uses Make-context to look more natural: } same-context?: func [ {find out, if the given Words are the Words of the same Context} word1 [word!] word2 [word!] ] [ either special-context? word2 [ special-context? word1 ] [ same? word1 bind in make-context reduce [word1] word1 word2 ] ] ; Model of CQSB function: ; ***************************** cqsb-function!: make object! [ ; every cqsb function has got a Spec attribute spec: none ; every cqsb function has got a Body attribute body: none ] ; Model of CQSB Func function: ; ********************************* cqsb-func: func [ {create a CQSB-function!} spec [block!] body [block!] /local spec-too body-too ] [ spec-too: copy spec body-too: copy/deep body make cqsb-function! [ spec: spec-too body: body-too ] ] ; Model of the CQSB function body execution: ; ******************************************* ; the same as for Sim-function do-body: func [body] [do body] ; Model of CQSB function Evaluation: ; *********************************** cqsb-evaluate: func [ {evaluate a sim-function contained in a block with its arguments} block [block!] /local cqsb-f new-context actual-values ] [ ; evaluate the arguments block: reduce block ; the executed cqsb-function is first cqsb-f: first block ; create a new Context new-context: make-context cqsb-f/spec ; give new Context words the supplied values set/any bind/copy cqsb-f/spec in new-context 'self next block ; execute the function body and return the result return do-body bind/copy cqsb-f/body in new-context 'self ] ; Some tests: ; ************** blk: copy [] probeblk: func [] [ prin mold blk prin ": " print mold reduce blk ] recfun: cqsb-func [x] [ append blk 'x either x <= 1 [ probeblk ] [ cqsb-evaluate [recfun x - 1] ] ] cqsb-evaluate [recfun 3] probeblk { Results:
>> cqsb-evaluate [recfun 3]
[x x x]: [3 2 1]
>> probeblk
[x x x]: [3 2 1] This shows, that "Pure CQSB" looks better than "CQSB with DRP". Recursion is not the only case, where "Pure CQSB" looks better. For another example, see this: } f: func [x] ['x] y: f 1 z: f 2 get y { Results:
>> get y
== 2 While Pure CQSB yields: } f: cqsb-func [x] ['x] y: cqsb-evaluate [f 1] z: cqsb-evaluate [f 2] get y { and the results:
>> get y
== 1 But, as always, everything has got its price. Pure CQSB is more demanding (every call needs a new Context/Binding). Moreover, the above implementation is not GC-bug proof. } to be continued...

 [14/17] from: lmecir:geocities at: 26-Jul-2000 22:36


Hi,
> HI Ladislav > > Very cool! Use of simulated behavior (implemented in REBOL) is
a concise
> and precise way of expressing one's thoughts about the workings
of REBOL
> functions. I find it much more informative than lengthy
attempts to
> describe in ordinary language how functions work. > > I have followed your Words, Bindings, and Contexts posts with
great
> interest. I have learned a lot from your discussions and hope
to see more.
> Just a couple of quick questions: > Can you say more about Dangerous Contexts? For instance:
<<quoted lines omitted: 4>>
> == word! > >>value? first first :f ;REBOL crashes on any attempt to
examine the
> words in first :f > > Isn't this just a bug in REBOL? Why does it not return an error
instead of
> crash? > > How about a modifying to sim-func to allow handling of /local
and
> refinements? Then it would be a more complete model of func. > > Thank you again for sharing this excellent material. > > -Larry >
1) I think, that every Rebol Context becomes Dangerous after being Garbage Collected while still accessible, ie. Dangerous Contexts may be just "Ghost Contexts" of once normal Rebol Contexts... 2) My intent was to describe the Rebol Context Handling and the refinements would complicate the code beyond the limit I considered acceptable. (BTW, if you consider Sim-function/spec as containing all Function Context's Words ie. Arguments, Refinements, Words specified as /local, everything holds, the things that should be added are the examination of a call-path used, a code to supply None as the value for respective Function Context's Words, a code for spec parsing and the type checking code.)

 [15/17] from: deadzaphod:h:otmail at: 27-Jul-2000 3:00


>l> I see, that the fact, that my series didn't explain the >l> behaviour of functions WRT Recursion and Binding is a flaw. l> Here is
<<quoted lines omitted: 3>>
>OTOH, I'd like to understand why Carl considers the current behaviour a >bug, while I was convinced this was by design.
Me too... I rather like the current behavior and have been writing scripts that will probably break if it is changed. I feel that the current STATIC binding is an excellent feature, and even if it was a bug, it should be kept and documented. - Cal ([deadzaphod--hotmail--com])

 [16/17] from: lmecir:geocities at: 29-Jul-2000 17:01


; Model of Use behaviour: ; *********************** sim-use: func [ "Defines words local to a block." words [block! word!] "Local word(s) to the block" body [block!] "Block to evaluate" /local context ] [ if word? words [words: reduce [words]] ; create the Context context: make-context words do bind body in context 'self ] { The described behaviour has got its drawbacks (See Bug in Use?). There is a possibility to improve the behaviour, see the next model: } r-use: func [ "Defines words local to a block." words [block! word!] "Local word(s) to the block" body [block!] "Block to evaluate" /local context ] [ if word? words [words: reduce [words]] ; create the Context context: make-context words do bind/copy body in context 'self ] { The last subject left is the behaviour of make object!. Here it is: } make-object!: func [ {make object! simulation} spec [block!] /local words result ] [ words: make block! 0 foreach elem spec [ if set-word? get/any 'elem [ append words to word! elem ] ] result: make-context words result/self: result do bind spec in result 'self return get/any in result 'self ] { Analogically as above, this code is not well-behaved in some circumstances. I would prefer the following: } r-make-object!: func [ {make object! simulation} spec [block!] /local words result ] [ words: make block! 0 foreach elem spec [ if set-word? get/any 'elem [ append words to word! elem ] ] result: make-context words result/self: result do bind/copy spec in result 'self result ]

 [17/17] from: lmecir:geocities at: 29-Jul-2000 20:27


My apologies to everybody. The Make-object! and R-make-object! functions shall be repaired (missing colon before Elem in append words to word! :elem) The correct versions: make-object!: func [ {make object! simulation} spec [block!] /local words result ] [ words: make block! 0 foreach elem spec [ if set-word? get/any 'elem [ append words to word! :elem ] ] result: make-context words result/self: result do bind spec in result 'self return get/any in result 'self ] r-make-object!: func [ {make object! simulation} spec [block!] /local words result ] [ words: make block! 0 foreach elem spec [ if set-word? get/any 'elem [ append words to word! :elem ] ] result: make-context words result/self: result do bind/copy spec in result 'self result ] { here is a code that can behave "unexpectedly": } f: func [level] [ make object! [ a: 2 * level b: either zero? level [ f 1 ] [ none ] a: a + 1 ] ] probe f 0 { The results:
>> probe f 0
make object! [ a: 0 b: unset ] } f: func [level] [ r-make-object! [ a: 2 * level b: either zero? level [ f 1 ] [ none ] a: a + 1 ] ] probe f 0 { The results:
>> probe f 0
make object! [ a: 1 b: make object! [ a: 3 b: none ] ] }

Notes
  • Quoted lines have been omitted from some messages.
    View the message alone to see the lines that have been omitted