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

[REBOL] Re: Context - code included- 2nd version

From: holger:rebol at: 12-Sep-2001 17:39

On Wed, Sep 12, 2001 at 11:26:57PM +0200, Ladislav Mecir wrote:
> The latest version of reflects some > points made in the latest discussions.
Unfortunately your major misconceptions are still in there. The highlights: - Words do not "store" values, they ARE values. REBOL is a first-class language, and a word is a value like any other value. Values may have properties that distinguish different values of the same type from each other, e.g. integer! values may have different numeric values. The properties of word! (and lit-word!, set-word!, get-word!) are, precisely, a reference to the context the word is bound to (if any) and its spelling. When evaluating a value the result is, usually, another value. The value you get when evaluating a word is NOT a property of the word, it is something determined dynamically by the interpreter, at the time the word is evaulated. Those values are not stored in words, they are stored in contexts. - Contexts are not word tables, but name-value lookup tables. The "names" are the spellings of words. The "values" are ordinary REBOL values, and are what you get when you evaluate a word with the same spelling that is bound into the context. - There is no "Special Context" and there are no "Special Words". What you call a "Special Word" is in reality a word not bound into a context. - Your distinction between local, global, special etc. words is a little confusing, mostly because "Loaded Word" is orthogonal to the other definitions. It refers to the existance of a word in the global context, whereas all the other definitions refer to the binding of an individual word. Better, more consistent with the documentation (not to mention implementation): Replace "Special Word" with "word not bound into a context". Replace "Loaded Word" with "word which exists in the global context". Replace "Global Word" with "word bound into the global context". Replace "Local Word" with "word bound into a non-global context". These are the major problems with your document. A lot of the complexity and many of the individual observations you made seem interesting and significant only because your somewhat arbitrary definitions obscure the actual meaning. Once those terms are replaced by their meanings most observations follow logically and become redundant. Some more minor nitpicks: - to-block! does not bind words into a context. make word! and to-word bind all words into the global context (terminology). - The global context expands automatically as new words are bound into it. The fact that this increases the number of "Loaded Words" is redundant (again, result of obscure terminology). - Aliases are not properties of contexts and therefore do not show up in visualizations of contexts. That's because aliases are global, not local per context. - Scoping: REBOL uses lexical scoping, not dynamic scoping, but allows words to be rebound during block evaluation. That's how words can refer to different contexts even though their scope is lexical. "Scope hierarchies" imply some kind of tree, which does not exist, so using that term is probably a bad idea. Calling a scope hierarchy "virtual" is yet another definition which obscures facts rather than illustrates them. The facts are: words live inside of blocks. When a block is created all words in it are either bound into a context, or remain unbound. Certain functions ('use etc.) change the binding of words. That is all there is to it, and the "scope" of a word at any given time is simply determined by the last bind operation on it before the word gets evaluated. - The "bug" you described when combining 'use with functions and other contexts is not a bug. It is intentional. There are only four ways how a function can (theoretically) access its local variables and arguments, in any language: - Using a hidden context pointer. That's what C does, using the stack pointer. REBOL does not (and cannot) do it, because the only local environment REBOL has when executing a function body is that particular function body, which is a simple block, and the association from a function to its block is one-way (like all references in REBOL). This means the function body has no hidden reference back to the function itself, and therefore cannot access its arguments. - By creating a completely new "environment" for each function. That includes a new argument and variable space (context) AND a new function body (deep-copied). This way all words in the function body can be rebound into the new context. That's what your 'cfunc does, but it is unacceptable performance-wise. - By keeping a single function body for all function invocations, and dynamically rebinding it to a different context, one for each invocation, every time a function is called or returns. This generates O(n) complexity for each function call, where n is the size of the function, i.e. performance is prohibitive as well for large functions, and it has other practical problems. - By keeping a single function body for all function invocations, bound to the same context, and changing the context on the fly (i.e. exchanging the "value" part of the name-value table for each function call, using a virtual stack). This is what REBOL does. The "bug" you describe is a normal and inevitable side effect of this, and not a "bug" at all. Your "fix" changes the function execution model in a way that generates unacceptable performance. If you really need the behavior you describe then you are, of course, free to write your own functions which implement it, as you have done, but for performance reasons that behavior should not be the default in REBOL. - The drawback you describe in regard to what you call the Dynamic Recursion Path is actually caused by the fact that the context of the outer function remains intact (indefinite extent) AND retains its values after the outer function returns (for non-recursive function invocations), under certain circumstances. This is an implementation detail you should not rely on, because it is likely to change in future versions, i.e. there is a good chance that in the future after a (non-recursive) function invocation the values of that function's context are set to unset!. This would break the way your inner function references the outer function's arguments after the outer function has returned. The problem with function contexts keeping their values after the function returns is that the referenced values do not get garbage-collected, even though no (apparent) reference exists any more, until the function is called again. In some situations (e.g. when trying to close and garbage-collect an unreferenced port) this can be very problematic. Relying on the values of a function's context beyond the life time of the corresponding function call should be considered illegal, i.e. you need to, e.g., 'reduce the body of your inner function so the argument gets replaced by its value before the function is returned. This also resolves the drawback you mentioned, i.e. use f: func [x] [func [] reduce [x]] and the behavior is as expected and compatible with future versions. -- Holger Kruse [holger--rebol--com]