# Rebol Programming/Advanced/Bindology

Author: Ladislav Mecir

## Acknowledgments

I would like to thank the men that influenced this article in some way. Those are especially Galt Barber, Brian D., Mark Dickson, Elan Goldman, Brian Hawley, Gregg Irwin, Thomas Jensen, Pierre Johnson, Holger Kruse, Volker Nitsch, Larry Palmiter, Patrick Philipot, Gabriele Santilli, Carl Sassenrath, Frank Sievertsen and Romano Paolo Tenca. Any errors are my copyright.

## References

The code in this article was tested in Rebol/View 2.7.6.3.1. It is possible that other versions of the interpreter yield different results.
A reader who wants to check all the examples can run this code:
, which defines all functions from this article.

## Word types

Rebol words like all Rebol values have type. Let's have a look at all available word types:
Moreover, all Rebol words have a common pseudo-type ANY-WORD!:

## Spelling

Every word has got a spelling. The spelling of a word is a string and it is one of the properties the above example words have in common. We can obtain the spelling of a word using the TO-STRING function:
Observation (Unusual spellings): Words normally don't have some spellings like spellings containing spaces, spellings starting with a colon, etc. On the other hand, it is possible to create a word having any spelling as follows:
Observation (Spelling and word equality): Words with strict equal spelling are equal.
Illustration:
The reverse implication does not hold (since Rebol supports aliases). Nevertheless, we can reverse the implication at least in some way.
Observation (SAME? and spelling): If two words are the same according to the SAME? function then they have got strict equal spelling.

## Variables

A very important property of words is the ability to serve as variables (refer to Rebol values). To set a variable 'rebolution to refer to a Rebol string "uprising" we can pick one of the following:
etc.
To get the value a variable refers to we can pick one of the following:
etc.
Observation (Variables): The ability of words to serve as variables is closely related to a word property called binding. A word is a variable if and only if it is bound to a context (has a context, is in a context).
Illustration:
The easiest way how to find out if a word is a variable is to use the properties of the BIND? function, which returns NONE for words that aren't variables:
Tests:
Observation (Context uniqueness): As the BIND? function helps us to find out, for every Rebol word there is at most one context the given word is bound to.
Corollary (Context hierarchy): From the above observation follows that in Rebol there is no context hierarchy, because a context hierarchy would require some word being bound to at least two distinct contexts, one being "smaller" than the other.
Observation (Word sameness): According to the SAME? function two words are the same if and only if they have strict equal spelling and equal binding.
Observation (The result of the BIND? function): When the BIND? function is used to obtain the context of a word in an object, the result of the BIND? function "is not considered identical" with the object.
Illustration:
Observation (Equal words need not have equal binding): In fact, the opposite is true. For any word we can create a word having strict equal spelling, equal type and different binding.
Let's test, whether the function does what we declared:
The test shows that WORD1 and WORD2 have strict equal spelling and equal type. Their bindings are different. They can refer to different values at the same time, so they are distinct variables. Another test:

## The BIND function

When we need to get a word having the spelling and type a given WORDS word has and the context a given KNOWN-WORD has, we can use the BIND function.
This is how the BIND function works:

### Binding to no context

Observation (Binding to no context): If the KNOWN-WORD has no context, BIND causes an error.
Illustration:

### Binding when the WORDS argument is a word

Observation (Effective bind): If it is possible, the BIND function yields a word having the spelling and type of the WORDS argument and the context of the KNOWN-WORD argument.
Illustration:
We observe that the result has got the spelling and the type of the WORDS word, but it isn't the WORDS word because it is bound to the same context as the KNOWN-WORD is.
Observation (Equal words and effective bind): If WORD1 is a variable having a context CONTEXT and WORD2 is equal to WORD1 then WORD2 can be bound to CONTEXT too.
Observation (Ineffective bind): In case the BIND function cannot yield a word having the spelling and type of the WORDS argument and the binding of the KNOWN-WORD argument, BIND returns the WORDS argument.
In this case BIND simply returned the WORDS word.

### Binding when the WORDS argument is a block

Observation (Binding block without copying): If the /COPY refinement isn't used, BIND replaces the elements of the block by results of their binding. There is one exception to this rule: BIND doesn't bind refinements contained in the block.

## Aliases

Definition (Aliases): Equal words that do not have strict equal spelling we call aliases.
This is our definition translated to Rebol:
Corollary (Word equality): Two words are equal if and only if one of the following conditions holds:
* the words have strict equal spelling
* the words are aliases
Corollary (Aliases and SAME?): The SAME? function yields FALSE when comparing two aliases.
Proof: See the (SAME? and spelling) observation and our definition of aliases.
Observation (Automatic aliases): Since Rebol tries to be case-insensitive, the interpreter usually (except for inconsistencies) "considers" words with spellings differing only in case to be aliases.
Observation (ALIAS): Aliases can be defined explicitly using the ALIAS function.
Observation (ALIAS return value): The ALIAS function returns unbound words.
Observation (Variable sameness): Two words are one variable if and only if they are equal and their bindings are equal too.
Observation (Alternative definition of ALIASES?): According to our previous observations this definition is equivalent to our original definition:
The alternative definition looks more complicated, but it is faster due to the fact that it does not need to manipulate strings.

## Context words

The BIND? function allows us to find the context of a given word. The reverse task is a task to find all words that are in a given CONTEXT context. It can be done as follows:
Observation (Simplified set of context words): The block obtained as the result of the first context expression is a simplified set of context words. As opposed to the result of the above function it contains unbound words. Moreover, similarly as the result of the above function it doesn't contain aliases of its words and it contains only words of the WORD! datatype.
Illustration:

## The global context

Definition (Global context): The global context can be defined e.g. as follows:
Note: This is not the only option, another one is to define it as the SYSTEM/WORDS object. The above definition gives us the simplest possible definition of global words.
Definition (Global words/global variables): The words that are bound to the global context we call global words (global variables):
Observation (MAKE, TO, LOAD, BIND and the global context): The words created by MAKE WORD!, MAKE SET-WORD!, MAKE GET-WORD!, MAKE LIT-WORD!, MAKE REFINEMENT!, TO WORD!, TO SET-WORD!, TO GET-WORD!, TO LIT-WORD!, TO REFINEMENT!, LOAD and BIND WORD 'SYSTEM are global.
Illustration:
Observation (Automatic growth): The global context can be enlarged using the MAKE, TO, LOAD and BIND functions. On the other hand, the IN function doesn't enlarge the global context.
Observation (MAKE, TO and unbound words): All words contained in a result block of MAKE BLOCK!, TO BLOCK! and in its subblocks are unbound if the SPEC argument is a string.
Illustration:

## Local contexts

Definition (Local words/local variables): The words that are neither unbound nor global we call local words (local variables):
Definition (Local context): A context is called local context if its words are local words.
Observation (Local context types): User defined objects, function contexts and USE contexts are local contexts. In addition to these we can use the BIND? function to "convert" user defined objects and ports to contexts and using the DISARM function we can convert errors to objects. All the results are local contexts. The main distinction between the function- and USE- contexts and all other context types lies in the fact that the function- and USE- contexts don't need to contain a word equal to the word 'self.
Observation (Enlarging local contexts): Local contexts aren't enlargeable.
Observation (The result of the DIFFERENT-BINDING function): The result of the DIFFERENT-BINDING function as we defined it above is always a local word.

## Computed binding

Let's observe the behaviour of the Rebol interpreter when evaluating an example code string:
The COLORIZE function will colorize the code listing as follows:
* the unbound words will be brown
* the global words will be blue
* the words bound by the first USE evaluation will be red
* the words bound by the second USE evaluation will be magenta
Let's watch how the code is being interpreted:
The result (pasted here from the clipboard) is:
| Text | Code |
| String to block conversion | ['f 'g 'h use [g h] [colorize "USE 1" 'f 'g 'h use [h] [colorize "USE 2" 'f 'g 'h]]] |
| Code block bound to the global context | ['f 'g 'h use [g h] [colorize "USE 1" 'f 'g 'h use [h] [colorize "USE 2" 'f 'g 'h]]] |
| USE 1 | ['f 'g 'h use [g h] [colorize "USE 1" 'f 'g 'h use [h] [colorize "USE 2" 'f 'g 'h]]] |
| USE 2 | ['f 'g 'h use [g h] [colorize "USE 1" 'f 'g 'h use [h] [colorize "USE 2" 'f 'g 'h]]] |
The result proves that:
* After the string to block conversion all words in the CODE-BLOCK were unbound
* After the CODE-BLOCK was bound to the global context all words in the CODE-BLOCK were global
* The first USE call replaced all words 'g and 'h from its body block and its subblock with local USE 1 words
* The second USE call replaced the 'h in the innermost block with the local USE 2 word
Observation (Computed binding): During the interpretation the binding of Rebol words contained in the code is changed (i.e. the words are being replaced by words with different binding) until they are bound correctly and evaluated. That is why the creator of Rebol calls this behaviour "Computed binding".

## Scope

It looks like we observed a "scope hierarchy" during the execution of the above Rebol code. As we have demonstrated, it was only a side effect of the computed binding.
With a help of the computed binding we can easily create code samples, which do not exhibit any "scope" property:
The CODE-BLK example demonstrates that for a code block there is no such thing as its "current context" in Rebol because in Rebol only individual words are associated with context.

## The USE function

To be as precise as possible we will write the description of the USE function behaviour in Rebol.
The following function creates a new context, in which all words are unset:
The description of USE:
Observation (USE-MODEL and BODY): USE-MODEL modifies its BODY argument when it binds it to the new context. If we wanted to leave the BODY argument unmodified, we should have used BIND/COPY instead of the present BIND.
Let's compare USE-MODEL's behaviour and the behaviour of USE:
As we made sure, the same is true for both USE-MODEL and the original USE. The simulation is so accurate that it helped us to reveal a bug in the following code:
Explanation/correction:
The observed USE property caused that the body of the function F got modified during the second USE execution. After that modification it no longer contained the word 'a that was set to "OK" during the first invocation of F. Instead it contained only the word 'a set to the "BUG!" value during the second invocation of F.
If we preserve the body of the F somehow, we can get the correct behaviour:
Another way how to correct the behaviour is to use our own version of USE that will not modify its body argument:

## MAKE OBJECT!

We need a function that evaluates the SPEC argument like MAKE OBJECT! does, which means that it has to catch RETURN, THROW and BREAK:
The MAKE OBJECT! simulation:
Observation (MAKE-OBJECT!-MODEL and SPEC): MAKE-OBJECT!-MODEL modifies its SPEC argument when it binds it to the new context. If we wanted to leave the SPEC argument unmodified, we should have used BIND/COPY instead of the present BIND.
The described behaviour leads to a bug similar to the bug described in the USE section:
The explanation and the correction are similar as for the USE function. The word a: in the a: "OK" line positioned after the recursive call to F and bound to the object F created first was replaced by a word a: bound to the object F created during the recursive call. Consequently, the expression a: "OK" had no effect on the object F created first and therefore it retained the last value of 'a, which was "BUG!". If we preserve the body of F, we can get the correct behaviour:
As you might have seen, the above code deep copies the BODY block before binding it to the context. Analogical bugs were discovered when the deep copying was not used when the FUNC function created Rebol functions.

## MAKE PROTO

This is a simulation of the situation when the MAKE function obtains a prototype of the object it has to create. First of all, we need a special BIND-like function:
And here is the simulation:

## Functions with MAKE OBJECT!-like handling of local words

Before we try to model the function evaluation, we can ask whether we can use the same method of the local words handling as the CONTEXT function uses.
The answer is positive and the function able to do this is defined below.
First of all a function that can extract all local words of a function from its SPEC:

## Model of Rebol functions

Our model of Rebol functions will be a Rebol object FUNCTION!-MODEL having the appropriate attributes. The totally necessary attributes of Rebol functions are SPEC and BODY.
To model the current behaviour of Rebol functions accurately our FUNCTION!-MODEL needs CONTEXT, CONTEXT-WORDS and RECURSION-LEVEL attributes to model the behaviour of Rebol functions during recursive calls:

### Model of the FUNC function

This function gets the SPEC and BODY attributes, creates a new FUNCTION!-MODEL and initializes it.

### Model of the function call stack

The call stack is empty when the interpreter starts.

### Model of function evaluation

Our simulation begins when the values of the function arguments are collected and their types checked.
The evaluation function obtains a FUNCTION!-MODEL together with a block of values it shall store to its local context words (i.e. all values of its arguments, optional arguments, refinements and local words).
We model only the morst frequent case of a function without the THROW/CATCH attributes.
The first part of our model executes the body:
The simulation:
Our model uses just one context for the whole lifetime of the FUNCTION!-MODEL without need to change the binding of its BODY. I call this behaviour a Dynamic Recursion Patch.
Some tests:
If we compare the simulated behaviour with the real Rebol functions, we get:
This shows that the simulation is really accurate and that Rebol functions use the Dynamic Recursion Patch too.
Although the Dynamic Recursion Patch can speed up the evaluation under some circumstances it has got its drawbacks:

## Computed binding functions (Closures)

As we have seen above Computed binding has got its merits while the Dynamic recursion patch isn't ideal. The results inspired me to implement the Computed binding functions and compare their behaviour with the behaviour of the Dynamic recursion patch functions.
The Computed binding functions shall create a new context every time they are called and bind their bodies accordingly. We can even use a part of the above simulation to implement them.
The first test:
This surely looks better than before. The second test:
The end.