Reva: Contexts

Introduction

Those of you familiar with ANS Forth, know about "VOCABULARY"s. A "context" is similar in that it encapsulates a list of words, and controls access to those words. It is different primarily in that Reva contexts also permit custom "find" implementations on a per-context basis. The idea for Reva's contexts comes from HelFORTH, but our implementation is different in subtle ways.

Contexts:

The custom "find" opens up the possibilities of:

Architecture

At the lowest level, a context is just a memory-area which stores some state information and pointers to other information. Each one has its own value of last, so that in fact each is an independent linked-list of (dictionary entries for) words.

Invoking the context places it on top of the list of active contexts, the "search-order". The word (find) knows to search each context in the search-order, from most recently added to the least recently added. All active contexts will be searched until the word sought is found.

Invoking exit~ removes the most recently invoked context from the search-order, and next context in-order then becomes top-most, and will be searched first. Unlike ANS, there is no way to rearrange search order, nor is there any need to. It is possible to look for a word in any specific context, whether or not it is in the list of active contexts - as long as one can find that context.

Words

These words implement contexts under Reva:

context: ( <name> -- )
Creates a new context, in the currently active context. Thus it is possible to "nest" contexts if that is desired.

context-name ( -- )
Makes that context "context-name" the active (top-most) one.

exit~ ( -- )
Pops the current context off the search-order. The prior context is now top-most.

~ ( -- )
The base context, with the core Reva words in it. It is always the first context, meaning it is the last one searched. So by putting a similarly named word in another context and using that context, one may override the word of the same name in the ~ context, temporarily.

in~ ( <context> <word> -- )
Look up <word> in <context> rather than using the normal search order. The <context> must be visible in the search-order.

to~ ( <context> <word> -- )
Transfer <word> from the current context to the named <context>

with~ ( <context> -- )
Puts <context> in the search-order, under the top-most. Essentially a "tuck" for contexts. This allows one to access the words in <context> without making new definitions appear in it.

without~ ( -- )
Undoes the effect of with~, basically like a "nip" for contexts.

reset~ ( -- )
Resets the search order to only have the ~ context

only~ ( <context> -- )
Makes <context> the only active context.

push~ ( -- )
Saves the current search order for a subsequent pop~

pop~ ( -- )
Restores the search order saved a previous push~

setfind~ ( xt <ctx> -- )
Set the word used for (find) in that context. This is an extremely powerful feature, and can be used, for example, to make a context use 'case-insensitive' matching, or to implement a 'dynamic lookup' for words.

.~ ( -- )
Lists the current search order (top of search order to the left)

.contexts ( -- )
Lists all contexts which have been defined, whether visible or not

xwords ( -- )
Shows all words in all contexts

reva ( -- )
Sets the "default" context order, which mostly allows one to ignore contexts in most cases.

Usage and recommendations

It is the Reva custom to prepend a tilde ~ to differentiate contexts from other words. This is not required, but is a recommended standard. Words which operate on contexts have a trailing tilde, whereas words prefixed with a tilde are contexts.

As an example of how one might use contexts:

 context: ~editor
 | We are still in the ~reva context.  New words are still in ~reva
 variable a  | in the ~reva context
 34 a !
 
 ~editor   | now we have put "~editor" as the top context
 variable a  | in the ~editor context, does NOT make the other one invisible
 12 a !
 exit~	| flipped back to the ~reva context
 
 | Test the results:
 a @ .  | will give "34"
 in~ ~editor a @ . | will give "12"

 ~editor
 a @ . | prints "12"
 in~ ~ a @ | prints "34"
 exit~

Playing well with others

Libraries which create contexts or add words to contexts, should be careful to restore the search order to what it was before the library was loaded. For example, lib/floats implements the "~floats" context, so it calls "push~" before doing its job, and "pop~" when it is finished loading.

This helps the programmer remain in complete control of access to the words; he or she can add a context as needed to the search list, and not worry about side-effects of loading libraries.

If you have private words, consider putting them in the "~priv" context, or in a "-private" context. So for example, "~floats-private" would be the place to put auxiliary words needed for the "floats" library, which might be useful to others as well. This clearly flags those words as "internal", yet allows access to them if needed.

For a real application, it is a good idea to create a context, say "~app", and use it for the application specific words.

Pre-existing contexts

Reva ships with several contexts "in core":

Various libraries add their own contexts. For example, "math/floats" has "~floats". "db/sqlite" has "~db", etc. See the documentation for each library to find out if you need to use a specific context to access the words it provides.

Dot notation

Generally, to access the word foo in context bar, one would put "bar" in the context order, and then simply type foo. Since it is not always convenient or desirable to put a particular context in the search order, Reva permits the syntax bar.foo, which means "get the word 'foo' in the context 'bar'". This syntax is handled by using a word? hook, meaning that it is a last-resort check. In other words, if you create a word called bar.foo, then you will not be able to use the "dot notation" to access the "foo" word.