auteur: Patrick Philipot date: 10-mar-2002, 11-mar-2002 version: 1.01
The purpose of this document is to enlighten the notion of 'bind. This baby gave me much trouble (and I am expecting more to come...).
Life is not always easy for one that embraces Rebol. The language is brilliant, elegant and powerful, but it has its share of gotchas and obscure or poor documented features... However when the dark side is frightening, you can always expect light coming from the rebolisters (the members of the rebol-list).
Thanks to Joel, Ladislav, Gregg, Carl, Romano, Gabrielle, Andrew, Brett, Alan, Maarten, Petr, Allen, Robert, Sunanda, Jason and many others, I have always found answers to my questions. My goal here is to make available what I have learned from these answers.
Vive la rebolution !
Let's try a very simple test :
>> for x 1 3 1 [print x] 1 2 3 >> print x ** Script Error: x has no value ** Where: do-boot ** Near: print x >>
We can see here that x has a value only within the for loop. Trying to use x outside the loop rises an error. We can say that x has a temporary existence and a temporary scope.
What happens if another x exists before the for loop, let's try it :
>> x: 100 == 100 >> for x 1 3 1 [print x] 1 2 3 >> print x 100 >>
The x which is defined before the for loop is in fact invisible inside the loop. The x inside the loop, and the x outside the loop, live in separeted contexts. The x outside lives in what is called the global context. This is the main Rebol context, the one where rebol words, such as 'print 'if or 'for, are stored and have meanings.
We can use Ladislav's s-c? function to prove that x is global (s-c? stands for same context). The function s-c? returns true if its two parameters are in the same context. Here we compare 'x with the 'print command which is a global rebol word :
>> s-c? 'print 'x == true >>
If x is in the same context than print, we can state that x is in the global context.
The source of s-c? can be found in Ladislav's contexts.html and at the end of this document
Is it possible however to access the global x inside the for loop ?
Yes, it is !
>> for x 1 3 1 [print rebol/words/x] 100 100 100 >>
What is rebol/words ? It is an object that holds all the rebol words.
>> type? rebol/words == object! >> first rebol/words == [end! unset! error! datatype! context! native! action! routine! op! function! object! struct! library! port! any-type! any-word!...
As any other objects, rebol/words is made of two blocks. The first block is a table of all its words.
We will note in passing that rebol/words is peculiar in that the table of all its words does not start with 'self.
In a recent post (5-mar-2002), Holger Kruse ( holger@rebol.net ) explained to the rebol-list that a context is internally made of two tables: a table of words and a table of corresponding values and that for object!s one can look at this two tables.
o: make object! [ a: 1 b: 2 c: 3 d: does [print [a b c]] ] >> first o == [self a b c d]
The first part is a table of all words.
>> second o == [ make object! [ a: 1 b: 2 c: 3 d: func [][print [a b c]] ] 1 2 3 func [][print [a b c]]...
The second part of an object is a table containing the corresponding values.
Previously we have seen that a global word can be made invisible inside a loop. Using an object, we can show that a global function can be made invisible too.
o: make object! [ print: func [b [block!]][rebol/words/print compose ["o-print-> : " (b)]] test1: does [ print ["test"]] ]
This object defines a function named 'print. As before, we have used the rebol/words path to access the global 'print function.
>> o/test1 o-print-> : test >>
When 'print is used inside the function, it refers to the local 'print .
Let's add a new function test2. The purpose of test2 is to 'do a given block of code.
o: make object! [ print: func [b [block!]][rebol/words/print compose ["o-print-> : " (b)]] test1: does [ print ["test"]] test2: func [b [block!]][do b] ]
Can we use test2 to print with our new print function ? let's try ...
>> o/test2 [print ["hello World!"]] hello World! >>
Good gracious, this is not the local 'print ! What happens ?
A quite simple rebol mecanism indeed: inside the block [print ["hello World!"]], 'print is bound to the global context and it will stay bound to it unless ... we use the 'bind command.
o: make object! [ print: func [b [block!]][rebol/words/print compose ["o-print-> : " (b)]] test1: does [ print ["test"]] test2: func [b [block!]][do b] test3: func [b [block!]][do bind b 'self] ] >> o/test3 [print ["hello World!"]] o-print-> : hello World!
The magic of 'bind has occurred. Every word of b is now bound to the same context as 'self.
The second argument of 'bind must be a word from the target context. 'self is a word that is available in any object! The list of all the object's word can be displayed like this :
>> first o == [self print test1 test2 test3]
We could have used any of the o object words for 'bind :
test3: func [b [block!]][do bind b 'test1]
test3: func [b [block!]][do bind b 'test2]
test3: func [b [block!]][do bind b 'test3]
test3: func [b [block!]][do bind b 'print]
When 'bind is performed on a block, effects are permanent.
>> myBlock: [print ["Hello World!"]] == [print ["Hello World!"]] >> do myBlock Hello World! >> o/test3 myBlock o-print-> : Hello World! >> do myBlock o-print-> : Hello World!
This effect is often not desirable, this is why 'bind is commonly given a copy of a block.
o: make object! [ print: func [b [block!]][rebol/words/print compose ["o-print-> : " (b)]] test1: does [ print ["test"]] test2: func [b [block!]][do b] test3: func [b [block!]][do bind copy b 'self] ]
Now the original block stays unchanged.
>> myBlock: [print ["Hello World!"]] == [print ["Hello World!"]] >> do myBlock Hello World! >> o/test3 myBlock o-print-> : Hello World! >> do myBlock Hello World!
'bind/copy deep copy the block before using it.
o: make object! [ print: func [b [block!]][rebol/words/print compose ["o-print-> : " (b)]] test1: does [ print ["test"]] test2: func [b [block!]][do b] test3: func [b [block!]][do bind/copy b 'self] ] myBlock: [print ["Hello World!"]] >> o/test3 myBlock o-print-> : Hello World! >> do myBlock Hello World!
When the /copy refinement is used myBlock is unchanged.
It is possible to 'bind a block again to the global context. All we need is a global word such as 'system.
>> do myBlock o-print-> : Hello World! >> bind myBlock 'system == [print ["Hello World!"]] >> do myBlock Hello World!
However I have never seen this method used anywhere.
'bind is not a guru function. In fact 'bind is often needed in every day situation. Some of these situations are presented here.
One can argue that defining a 'print function inside an object was not a very clever idea. Naming the function 'myprint for example would have been more simple. We can explore this path and see if it is true.
o: make object! [ myprint: func [b [block!]][print compose ["o-print-> : " (b)]] test1: does [ myprint ["test"]] test2: func [b [block!]][do b] test3: func [b [block!]][do bind copy b 'self] ] >> o/test1 o-print-> : test
This one passes the test.
>> myblock: [myprint ["Hello World!"]] == [myprint ["Hello World!"]] >> o/test2 myblock ** Script Error: myprint has no value ** Where: test2 ** Near: myprint ["Hello World!"]
This one fails because myprint is bound to the global context where it has no value.
>> o/test3 myblock o-print-> : Hello World!
Finally 'bind is again the solution.
This kind of construction is often used when creating a dialect. It is common then to build blocks where "dialect functions" must be used from outside. A good example can be found in the "TUI Dialect - A dialect to print ASCII sequences in REBOL" by Ingo Hohmann at http://www.rebolforces.com.
Exploring the rebol-list archives ( at http://www.escribe.com/internet/rebol/index.html ) I have found an interesting example of a every day situation involving 'bind.
The problem was exposed with a very small script by Fantam :
block: ["a" "b" "c"] code: [print member] foreach member block [do code] ** Script Error: member has no value ** Where: do-boot ** Near: print member
The explanation was given by Larry. The word 'member in 'code is bound to the global context (where it has no value yet). The word 'member in the foreach is a new word that is bound to the local context of the foreach loop. One solution is to bind the code to the local 'member.
>> block: ["a" "b" "c"] == ["a" "b" "c"] >> code: [print member] == [print member] >> foreach member block [do bind code 'member] a b c
The 'member in 'code is now bound to the loop context where it has a value.
The following is an example inspired by a post from Joel Neely on Thusday, 18 Oct 2001. It emphasizes the fact that binding does not alter every word of a block, but only those that have a local counterpart.
glorp: "I'm global" glunk: "I'm global too" wordblock: [glorp glunk] some-func: func [b [block!] /local glorp foo][ glorp: "I'm local" foo: 42 print [glorp] print b bind b 'foo print b ] >> print wordblock I'm global I'm global too
Every thing is global.
>> some-func wordblock I'm local I'm global I'm global too I'm local I'm global too
After binding 'glorp in 'wordblock is bound to the some-func context.
After binding 'glunk in 'wordblock is unchanged because there is no word with the same spelling in the context of 'foo.
>> print wordblock I'm local I'm global too
The change as always is permanent.
If you have an example of other 'bind usage feel free to send it to me at pat665@ifrance.com . Criticism, remark are also welcome, send them at bullshit-happens@ifrance.com (it's not a joke ... yeah ok it'is a joke but the address is real).
This function can be used to find out, if a Word is undefined:
undefined?: func [ {determines, if a word is undefined} word [any-word!] ] [ error? try [error? get/any :word] ]
Two Words WORD1 and WORD2 are bound to the same context, if the expression (s-c? word1 word2) yields TRUE.
s-c?: func [ {Are word1 and word2 bound to the same context?} word1 [word!] word2 [word!] ] [ found? any [ all [ undefined? word1 undefined? word2 ] all [ not undefined? word2 same? word1 bind use reduce [word1] reduce [ 'first reduce [word1] ] word2 ] ] ]
Documentformater copyright Robert M. Münch. All Rights Reserved.
Formatted with Make-Doc-Pro Version:1.0.0 on 11-Mar-2002 at 20:38:53