RPN in Factor


Factor is a stack-based, object-oriented programming language, created in 2003 and as of 2006 still actively evolving. It has deep roots in Forth, and retains apparent similarity with the latter, but also adds enough functionality to stand out as a substantially new language, significant in itself.

Unlike Forth, which only has a minimal set of datatypes and operations on them, Factor enjoys a rich datatype set, operations on bulk data, facilities for functional and object-oriented programming, concurrency and other features. Those are borrowed from elsewhere: Common Lisp, Joy, prototype-based object-oriented, array-oriented, and modern functional languages have been influential in the design of Factor.

Factor is applicable to many areas, and strives to be competitive in execution speed to top-performing languages such as C. It is more practically oriented than both its most influential ancestors, Forth and Joy, by being neither feature-minimalistic like the former nor of experimental nature like the latter. Apart from application programming, a possible use of Factor is as a host language in implementing domain-specific or even general-purpose languages. For this it has sufficient provision for low-level programming and also interfaces well with C libraries.

The structure and the general appearance of a Factor program exhibit a blend of Forth's word-centred and Joy's quotation-centred styles. A program is a sequence of items of two kinds. Literal values put themselves on the stack, while words (represented in the program by names) evoke specific actions associated with them. In particular, (code) quotations, being executable literal values, can be pushed onto the stack and executed at some time later on.

Although words in Factor have much in common with what they are in Forth, there are important differences as well. Factor's words carry much more information, including optional user-defined data. It is possible to query a word about its associated data. One can place accompanying code in a word for tracing and debugging purposes. Word binding is truly dynamic: redefining a word automatically makes all words that call it to refer to the new definition.

Parsing program's own input is customizable as in Forth, although the details are different. One can define her own parsing words to be executed at parse time. However, by default a word definition is not compiled to machine code: the corresponding reference and literal data items are only assembled in a sequence as they are. Compilation is possible upon explicit request, and that yields compiling the called words, too. Compiling leads to very significant gains in performance.

Words are grouped within vocabularies, themselves designated by names. Each word belongs to a vocabulary, and the latter must be included in the so called search path so that it can be looked up by the interpreter. The predefined words are dispersed over a large number of vocabularies, and only a few of those are in the search path of a program by default.

The operand stack (also called data stack) is the unique receptacle for operative data, but as a means of long-term data storage variables can be used. Variables are named values, clustered within namespaces. As a name can have different bindings in different namespaces, the so called namestack – a stack of namespaces – is used to define the order in which the interpreter looks up variables. Thus, the namestack is a set of dynamically (and explicitly) nested scopes for variable names. Namespaces and the namestack are otherwise just values, so they too can be manipulated programmatically.

Factor's built-in datatypes include unbound integers, rationals, and floating-point and complex numbers. As in other stack-based languages – and without any apparent reason in any of them – there is no character datatype in Factor. The so called collection types comprise of sequences, hash-tables, namespaces, queues, and – somewhat surprisingly – (directed) graphs. Of special significance are sequences and hash-tables, as they not only shape the style of application programming in Factor but also are used intensely for implementing some of the language's characteristic constructs, such as namespaces, the namestack, and the search path.

The language predefines several particular kinds of sequences (including strings and arrays) together with specific operations for each one. Some operations, however, are generic on sequences and resemble the list processing functions usually found in modern functional languages: for traversal, mapping, filtering etc. Since being a sequence amounts to conforming to a known procedural protocol, it is possible to implement user-defined sequences. All generic operations already present in the language are automatically applicable to the new sequences.

As data objects, quotations are an implementation of sequences, and so in every respect can be treated as such. Thus unrestricted, convenient means exist to create and manipulate anonymous procedures dynamically. (In this, Factor's quotations are similar to PostScript's procedures, i.e. arrays that can be executed.) In view of this feature, Factor represents (or can make use of) a concatenative functional style of programming, varying at will from its purest form as seen in Joy to any degree of mixed functional-with-side-effects expression mode. That the language favours a functional style of programming is accented by not providing any imperative loop constructs, which makes it deviate sharply from Forth. In fact, many programs can be expressed with neither loops nor recursion, using bulk operations.

User-defined datatypes and classes can be constructed in Factor in a similar but simpler way to that in Common Lisp, esp. its Object System. For example, one can construct a tuple: a class characterised by a set of named slots. Or one can create a new class by uniting or intersecting already existing classes, or by restricting an existing class through selecting a subset of its elements by means of a predicate. Generic method, method combination, and delegation mechanisms are provided. In particular, delegation is used to model inheritance.

Factor is one of the not so many languages to feature continuations as first-class program objects. Continuations are also the base for implementing the language's exception handling mechanism, and possibly play a similar role in other control-related internals.

Links of Relevance:

The home page of Factor. There are a Factor-related news page and an archived discussion group linked from there.