Hubbry Logo
Factor (programming language)Factor (programming language)Main
Open search
Factor (programming language)
Community hub
Factor (programming language)
logo
7 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Factor (programming language)
Factor (programming language)
from Wikipedia
Factor
Paradigmmulti-paradigm: concatenative (stack-based), functional, object-oriented
DeveloperSlava Pestov
First appeared2003
Stable release
0.100 / September 11, 2024 (2024-09-11)
Typing disciplinestrong, dynamic
OSWindows, macOS, Linux
LicenseBSD license
Websitefactorcode.org
Influenced by
Joy, Forth, Lisp, Self

Factor is a stack-oriented programming language created by Slava Pestov. Factor is dynamically typed and has automatic memory management, as well as powerful metaprogramming features. The language has a single implementation featuring a self-hosted optimizing compiler and an interactive development environment. The Factor distribution includes a large standard library.

History

[edit]

Slava Pestov created Factor in 2003 as a scripting language for a video game.[1] The initial implementation, now referred to as JFactor, was implemented in Java and ran on the Java Virtual Machine. Though the early language resembled modern Factor superficially in terms of syntax, the modern language is very different in practical terms and the current implementation is much faster.

The language has changed significantly over time. Originally, Factor programs centered on manipulating Java objects with Java's reflection capabilities. From the beginning, the design philosophy has been to modify the language to suit programs written in it. As the Factor implementation and standard libraries grew more detailed, the need for certain language features became clear, and they were added. JFactor did not have an object system where the programmer could define their own classes, and early versions of native Factor were the same; the language was similar to Scheme in this way. Today, the object system is a central part of Factor. Other important language features such as tuple classes, combinator inlining, macros, user-defined parsing words and the modern vocabulary system were only added in a piecemeal fashion as their utility became clear.

The foreign function interface was present from very early versions to Factor, and an analogous system existed in JFactor. This was chosen over creating a plugin to the C part of the implementation for each external library that Factor should communicate with, and has the benefit of being more declarative, faster to compile and easier to write.

The Java implementation initially consisted of just an interpreter, but a compiler to Java bytecode was later added. This compiler only worked on certain procedures. The Java version of Factor was replaced by a version written in C and Factor. Initially, this consisted of just an interpreter, but the interpreter was replaced by two compilers, used in different situations. Over time, the Factor implementation has grown significantly faster.[2]

Description

[edit]

Factor is a dynamically typed, functional and object-oriented programming language. Code is structured around small procedures, called words. In typical code, these are 1–3 lines long, and a procedure more than 7 lines long is very rare. Something that would idiomatically be expressed with one procedure in another programming language would be written as several words in Factor.[3]

Each word takes a fixed number of arguments and has a fixed number of return values. Arguments to words are passed on a data stack, using reverse Polish notation. The stack is used just to organize calls to words, and not as a data structure. The stack in Factor is used in a similar way to the stack in Forth; for this, they are both considered stack languages. For example, below is a snippet of code that prints out "hello world" to the current output stream:

"hello world" print

print is a word in the io vocabulary that takes a string from the stack and returns nothing. It prints the string to the current output stream (by default, the terminal or the graphical listener).[3]

The factorial function can be implemented in Factor in the following way:

: factorial ( n -- n! ) dup 1 > [ [1..b] product ] [ drop 1 ] if ;

Not all data has to be passed around only with the stack. Lexically scoped local variables let one store and access temporaries used within a procedure. Dynamically scoped variables are used to pass things between procedure calls without using the stack. For example, the current input and output streams are stored in dynamically scoped variables.[3]

Factor emphasizes flexibility and the ability to extend the language.[3] There is a system for macros, as well as for arbitrary extension of Factor syntax. Factor's syntax is often extended to allow for new types of word definitions and new types of literals for data structures. It is also used in the XML library to provide literal syntax for generating XML. For example, the following word takes a string and produces an XML document object which is an HTML document emphasizing the string:

 : make-html ( string -- xml )
    dup
    <XML
        <html>
            <head><title><-></title></head>
            <body><h1><-></h1></body>
        </html>
    XML> ;

The word dup duplicates the top item on the stack. The <-> stands for filling in that part of the XML document with an item from the stack.

Implementation and libraries

[edit]

Factor includes a large standard library, written entirely in the language. These include

  • A cross-platform GUI toolkit, built on top of OpenGL and various windowing systems, used for the development environment.[4]
  • Bindings to several database libraries, including PostgreSQL and SQLite.[5]
  • An HTTP server and client, with the Furnace web framework.[6]
  • Efficient homogeneous arrays of integers, floats and C structs.[7]
  • A library implementing regular expressions, generating machine code to do the matching.[8]

A foreign function interface is built into Factor, allowing for communication with C, Objective-C and Fortran programs. There is also support for executing and communicating with shaders written in GLSL.[3][9]

Factor is implemented in Factor and C++. It was originally bootstrapped from an earlier Java implementation. Today, the parser and the optimizing compiler are written in the language. Certain basic parts of the language are implemented in C++ such as the garbage collector and certain primitives.

Factor uses an image-based model, analogous to many Smalltalk implementations, where compiled code and data are stored in an image.[10] To compile a program, the program is loaded into an image and the image is saved. A special tool assists in the process of creating a minimal image to run a particular program, packaging the result into something that can be deployed as a standalone application.[3][11]

The Factor compiler implements many advanced optimizations and has been used as a target for research in new optimization techniques.[3][12]

References

[edit]
[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
Factor is a concatenative, stack-based programming language designed for practical software development, featuring dynamic typing, garbage collection, extensible syntax through macros, and an interactive environment that supports rapid prototyping and code reloading. Created by Slava Pestov, it employs postfix notation and a point-free style inspired by languages like Forth and Joy, while incorporating high-level abstractions such as object-oriented generic functions and metaprogramming facilities. Factor is fully compiled for performance, portable across major platforms including Windows, macOS, and Linux, and licensed under the BSD license as an open-source project. Development of Factor began in September 2003 as an embedded hosted on the (JVM), but it soon transitioned to a standalone native implementation due to JVM limitations in supporting low-level operations and interactive features. Over the years, it evolved incrementally into a mature , with key additions including a comprehensive object system modeled after CLOS, a checker for safer code, and optimizing compilation. The project remains actively maintained, with the latest stable release (version 0.100) issued in September 2024, and ongoing development hosted on . At its core, Factor operates on a data stack where words (functions) manipulate inputs and produce outputs, declared via stack effect notations such as ( path encoding -- seq ) to ensure and clarity during interactive use. It supports purely with single and multiple dispatch on generic functions, alongside low-level capabilities like foreign function interfaces (FFI), , and binary data handling for . The language emphasizes expressiveness through vocabularies—modular libraries that extend its core—covering areas from mathematics and strings to user interfaces and networking, with over 37,000 words in the . Factor's implementation includes a written in C++, generational garbage collection, and two compilers: a baseline for quick and an optimizing one for production code, achieving competitive performance on benchmarks such as . Its ecosystem facilitates standalone application deployment and interactive exploration via a listener and tools like the paved listener for . The community engages through a server and contributes to extensions, maintaining Factor's focus on practicality for domains like scientific computing, , and tooling.

Overview

Core Characteristics

Factor is a dynamically typed programming language that employs garbage collection for automatic memory management, allowing developers to focus on code logic without manual allocation and deallocation concerns. This approach uses a generational garbage collector, optimizing for workloads with many short-lived objects through mark-sweep-compact in the oldest generation and copying collection in younger ones. The language's type system supports dynamic dispatch while providing optional static checks via stack effect declarations, enhancing reliability in interactive development. At its core, Factor operates on a stack-oriented execution model that utilizes (RPN), where operations are postfix and arguments are passed via a data stack rather than named parameters. This concatenative paradigm enables terse, composable code but requires familiarity with stack manipulation to avoid errors. Factor supports multiple programming paradigms, blending concatenative programming with functional elements like higher-order functions and quotations (anonymous code blocks) and object-oriented features such as classes, , and polymorphism. These capabilities make it versatile for general-purpose application development, drawing influences from languages like Forth, , and Smalltalk. As an open-source project licensed under the BSD license, Factor is freely available and encourages community contributions. It provides native implementations for major platforms, including Windows, macOS, and on x86 and x86-64 architectures, ensuring portability without runtime dependencies. Deployment follows an image-based model, where the entire runtime state—including compiled code, data, and libraries—is saved to a single executable image file, facilitating quick startups and distribution of self-contained applications. This method, akin to systems in Smalltalk, supports incremental development and easy versioning of program states.

Design Philosophy

Factor's design philosophy draws heavily from Forth, adopting its stack-based, concatenative model for simplicity and expressiveness, while incorporating high-level abstractions such as dynamic typing and automatic garbage collection to mitigate the manual and low-level complexities often associated with Forth implementations. This evolution aims to balance the elegance of postfix notation with modern conveniences, enabling developers to focus on problem-solving rather than boilerplate concerns. At its core, Factor seeks to foster an interactive and extensible environment optimized for and full-scale application development, where code can be incrementally built, tested, and refined in a live REPL session without the need for frequent recompilation. The language emphasizes programmer productivity through a rich ecosystem of libraries and tools that support seamless integration across platforms, allowing for quick iteration from idea to deployment. Central to this philosophy is the of "words"—short, composable functions that serve as the fundamental building blocks of code, promoting readability and modularity within the concatenative by clearly declaring their stack effects (e.g., ( x y -- z ) for a word consuming two inputs and producing one output). This approach encourages the creation of domain-specific vocabularies, where words can be combined like bricks to form expressive pipelines, reducing cognitive overhead in complex computations. Factor explicitly rejects rigid, context-free syntaxes typical of imperative languages, instead favoring a macro system that enables runtime and compile-time extensions for defining custom, declarative notations tailored to specific problem domains, such as embedded DSLs for or UI layout. By treating syntax as programmable, Factor empowers users to evolve the language itself, aligning with its goal of ultimate flexibility without sacrificing the underlying stack discipline.

History

Origins and Early Development

Factor was created in 2003 by Slava Pestov as an embedded scripting language named JFactor, initially implemented in for a . The language was designed to provide a simple mechanism for scripting game logic within a larger Java application, leveraging a stack-based evaluation model to enable concise and efficient code for tasks like handling game entities and behaviors. This early incarnation emphasized ease of embedding and , drawing inspiration from Forth's concatenative while incorporating dynamic typing and garbage collection from the outset. From its , Factor adopted a Forth-like centered on stack manipulation, but Pestov integrated object-oriented elements, making every value an object to support modern programming practices. The stack-based approach allowed for postfix notation without explicit variables in many cases, facilitating a focus on composable operations suitable for scripting environments. This blend addressed limitations in existing languages for game development, where traditional often complicated dynamic behaviors. The Java-hosted implementation proved limiting for broader applications, lacking features like tail-call optimization and efficient , which prompted a transition to a standalone native in 2005. This shift was driven by performance requirements for more demanding programs and the goal of achieving self-hosting, enabling Factor to compile its own codebase independently of the JVM. The native version bootstrapped from the Java prototype, marking Factor's evolution from a niche scripting tool to a .

Major Milestones and Releases

In 2005, Factor's development shifted from its initial Java-based interpreter to a native runtime implemented in a combination of and Factor code, enabling the first self-hosted and replacing the pure Java implementation for improved performance and independence. This transition laid the groundwork for Factor's process, where core components like the parser and are written in the language itself. Between 2008 and 2010, significant enhancements included the introduction of an fully implemented in Factor, which generates for architectures like x86 and PowerPC through extensive . A cross-platform UI toolkit based on was added, supporting interactive development environments, while the standard library underwent major expansions for practical applications. Slava Pestov detailed the bootstrap process in contemporary posts, highlighting how an existing Factor image compiles updates to produce new images. From 2010 onward, following Pestov's primary leadership until 2010, the project has been driven by an active community, with key releases including version 0.97 in November 2014, which incorporated over 1,400 commits addressing bug fixes, library refinements, and integration improvements. Version 0.98, released in July 2018, further emphasized stability enhancements and concurrency support via coroutine-based multithreading in the single-threaded runtime. In August 2023, version 0.99 was released, adding features such as a Guided Tour for new users, 15 support, 3.1.2 and 3.42.0 bindings on Windows, and re-added support. These efforts focused on robustness for real-world use without altering core language semantics. In September 2024, version 0.100 was released, featuring modern platform compatibility like preliminary ARM64 support in the non-optimizing compiler, Unicode 15.1 integration, improved floating-point output, and automatic light/dark theme detection on Windows, alongside fixes for handling and library optimizations. This update reduced image sizes through compressed format support and resolved long-standing issues across Windows, macOS, and platforms. Ongoing maintenance of Factor is sustained by a community of contributors via the GitHub repository, ensuring compatibility updates and incremental improvements as of November 2025.

Language Fundamentals

Syntax and Stack Model

Factor employs a postfix notation, also known as Reverse Polish Notation (RPN), in which operands precede operators on a single data stack, eliminating the need for parentheses to denote precedence. For instance, the expression 2 3 + . pushes the literals 2 and 3 onto the stack, applies the addition word to pop them and push their sum (5), and then prints the result using the output word .. This stack-based execution model treats the data stack as the primary mechanism for passing arguments to and returning results from words (Factor's term for functions or procedures), with literals such as numbers, strings, and sequences directly pushed onto the stack when encountered. To document and verify the behavior of words with respect to the stack, Factor uses stack effect declarations in the form ( inputs -- outputs ), where inputs and outputs are listed from bottom to top of the stack (rightmost being the top). These declarations specify the number and types of values consumed from and produced onto the stack; for example, the addition word + is declared as ( x y -- z ), indicating it pops two numbers (y on top, then x) and pushes their sum. Stack effects support row variables like ..a for sequences of unknown length and nested declarations for quotations, enabling the compiler's stack checker to enforce correctness at compile time for inline words. Literals in Factor include primitive types like integers, floats, booleans (true as t, false as f), and strings, all of which are pushed directly onto the stack without additional syntax. Quotations, denoted by square brackets [ ... ], create anonymous functions or delayed code blocks that are also pushed as first-class values onto the stack; they can be executed later using words like call or passed to combinators such as map. For example, 3 [ 2 + ] call pushes 3, then the quotation that adds 2, and invokes it to yield 5. Basic control structures operate directly on stack values, such as conditionals where a boolean on top selects between two quotations via if: flag [ true-branch ] [ false-branch ] if, with the chosen quotation called and the other dropped. Vocabularies serve as namespaces for organizing words, allowing code to be modularized into hierarchical modules stored in directories. To access words from a , one qualifies them explicitly as vocab-name.word (e.g., math.+), or imports the entire vocabulary using USE: vocab-name for single imports or USING: vocab1 vocab2 ; for multiple, making words directly accessible without qualification. Factor eschews traditional variables in favor of stack-based computation for local state, with persistent or global state managed through object slots (fields in objects) or dynamically scoped variables that thread values across word calls without stack manipulation. Dynamically scoped variables, such as those for streams, are accessed via words like get and set, providing a way to share context implicitly during execution.

Words and Vocabularies

In Factor, the basic unit of organization is the word, which serves as a named procedure equivalent to functions in other languages. Words are defined using the colon definition syntax : word-name ( stack-effect ) word-body ;, where the declares the inputs and outputs on the data stack, and the word-body consists of Factor expressions that manipulate the stack. For example, the word to compute the square of a number is defined as : square ( n -- n ) dup * ;, which duplicates the top stack item and multiplies it by itself. This syntax encapsulates reusable stack manipulations, promoting modular construction. Factor supports words with lexical variables using the :: definition syntax, which allows naming input and output parameters in the stack effect for use within the word body. These provide a more imperative style within the concatenative framework. For example: :: square ( x -- y ) x x * ; defines a word where x is the input and y the output, both accessible by name inside the body. Local computations within a word can be encapsulated using anonymous quotations, such as : process ( data -- result ) [ 2 * ] call ;, which doubles the input data and leaves the result on the stack. Words are organized into vocabularies, which act as modular namespaces grouping related functionality to avoid naming conflicts and enhance code maintainability. Each vocabulary corresponds to a directory in the Factor source tree, containing files with definitions that load together. For instance, the math vocabulary collects arithmetic operations like + and sqrt, while sequences handles list manipulations such as map. In interactive development, the listener—a terminal-based REPL—evaluates expressions line-by-line, displaying the data stack after each input and facilitating rapid testing and loading of vocabularies with commands like USE: math or reload. This environment allows developers to define and invoke words incrementally.

Programming Paradigms

Concatenative and Functional Aspects

Factor's concatenative allows programs to be composed by simply juxtaposing words, which are the language's fundamental units of code, each performing a transformation on the data stack. This composition is implicit and point-free, meaning functions are applied without explicitly naming arguments or intermediate variables; instead, the stack serves as the medium for data flow, enabling concise expressions like dup square + to compute the square of a number plus the number itself, where dup duplicates the top stack item, square squares it, and + adds the results. Higher-order functions are supported through quotations, which are anonymous code blocks enclosed in square brackets that can be passed as arguments to combinators. For instance, the map combinator applies a to each element of a , producing a new , as in { 1 2 3 } [ sq ] map, which squares each number to yield { 1 4 9 }. Similarly, fold reduces a using a binary , such as { 1 2 3 } [ + ] fold summing the elements to 6. These features facilitate functional-style programming by treating as . Data in Factor is immutable by default in its functional core, with sequences and other structures typically returned as new values rather than modified in place, promoting referential transparency. Functional constructs like fold and higher-order combinators encourage pure transformations, while recursion is optimized through tail-call elimination, allowing efficient implementation of loops without stack overflow; for example, a recursive factorial can be defined using a tail-called word that accumulates the product. Pattern matching and destructuring are achieved through stack manipulation words, such as swap to exchange the top two items or dup to duplicate, enabling selective access and binding without explicit variable declarations; this stack-oriented approach integrates seamlessly with the concatenative model for concise data decomposition. To maintain purity, Factor supports subsets of code that avoid side effects by relying on stack transformations and combinators, excluding I/O or mutable state operations, which allows for verifiable functional behavior in critical sections.

Object-Oriented Features

Factor integrates concepts into its concatenative, stack-based paradigm through a lightweight object system centered on tuples and generic functions, enabling encapsulation, , and polymorphism without traditional message-passing semantics. This design draws inspiration from systems like CLOS, where behavior is dispatched based on object types rather than explicit method calls, allowing seamless extension within the stack model. Classes in Factor are primarily defined using tuples, which serve as user-defined data structures with named slots for storing state. The TUPLE: word declares a class, specifying its name and slots in the form TUPLE: <class-name> { slot1 slot2 ... } ;, where each slot holds an instance variable of a declared type. For example, a simple point class might be defined as TUPLE: point { x number } { y number } ;, creating a class word point for type checks and instantiation, along with automatic accessor methods for the slots. Instances are constructed explicitly, often via the boa word, which binds values to slots from the stack: 2 3 point boa yields a point instance with those coordinates. Slots encapsulate object state, with access controlled through generated reader (slot>>) and (>>slot) methods that push or set values on the stack while preserving . Read-only slots can be declared to prevent modification, enhancing in object designs. Methods in Factor are implemented as generic words, which dispatch to specialized implementations based on the classes of their inputs, providing polymorphism integrated with the stack effects. A generic word is declared with [GENERIC:] and methods added using M:, specializing on tuple classes; for instance, the + operation on numbers is a generic that selects methods matching the operand types. This approach enables protocol-based polymorphism, where objects adhere to behavioral protocols through shared generic methods rather than explicit interfaces, allowing extensible and composable object interactions without subclass proliferation. Inheritance supports hierarchical class relationships, with single inheritance achieved via direct subclassing or mixins. A subclass is defined as TUPLE: subclass < superclass { new-slots } ;, inheriting all slots and methods from the superclass while adding or overriding as needed. Mixin classes, declared with MIXIN: <mixin-name> < superclass ;, provide reusable behavior for single-inheritance scenarios, such as adding common functionality to unrelated hierarchies. Multiple inheritance is handled through union classes via UNION: union-class { class1 class2 ... } ;, combining methods from multiple parents with linearization to resolve ambiguities based on class precedence. Type checking is facilitated by predicate words, such as point? for the point class, which verify instance membership and support dispatch in generics; these predicates extend to subclasses and unions for polymorphic type queries.

Extensibility and Tools

Macros and Syntax Extension

Factor provides powerful mechanisms for extending its syntax through macros and parsing words, allowing users to define custom syntactic constructs that integrate seamlessly with the language's concatenative model. Macros, defined using the MACRO: word, perform compile-time transformations by replacing invocations with expanded quotations, enabling the movement of computations from runtime to compile-time for improved efficiency and abstraction. This facility supports the creation of higher-level idioms while preserving the stack-based evaluation semantics. Parsing words, declared with SYNTAX:, further extend syntax by executing during the parsing phase to read input tokens and manipulate the parse tree, such as by adding deferred actions via suffix!. These words, conventionally named in uppercase, operate on an accumulator to build domain-specific syntax without altering the core parser. The POSTPONE: word plays a crucial role in this process by appending the subsequent word to the parse tree literally, even if it is a parsing word itself, thus delaying its execution until runtime and preventing premature evaluation during macro or parser definition. For instance, POSTPONE: { can be used within a macro to include array literal parsing at expansion time rather than parse time. This combination allows for arbitrary syntax extensions, aligning with Factor's design goal of extensibility. Symbol and scanner words facilitate the construction of domain-specific languages (DSLs) by tokenizing input and generating custom literals or structures. Scanner words process sequences of tokens, while symbols serve as identifiers that can trigger specialized parsing; together, they enable embedded DSLs for tasks like XML literals, where <XML parses markup until a closing tag, producing a structured quotation. Integration with quotations is central to meta-programming: macros and parsing words generate or compose quotations that are spliced into the code, allowing dynamic code generation while the stack checker infers effects for optimization. To ensure hygiene and avoid name capture in macro expansions, Factor employs unique symbols generated by gensym, which creates uninterned words guaranteed not to conflict with existing bindings. This practice prevents unintended variable shadowing, as the generated symbols are distinct from any user-defined names in the surrounding scope. For example, in defining a macro that introduces temporary variables, gensym ensures isolation:

MACRO: temp-macro ( x -- y ) gensym temp { [ temp set x get + temp get ] } ;

MACRO: temp-macro ( x -- y ) gensym temp { [ temp set x get + temp get ] } ;

Here, temp is uniquely named, safeguarding against capture. Practical examples illustrate these features. For pattern matching syntax, Factor extends the language via macros that invert quotations using the undo combinator, enabling declarative matching on stack values. The case construct chains such inverses:

{ { [<nil>] [0] } { [<cons>] [sum +] } } case

{ { [<nil>] [0] } { [<cons>] [sum +] } } case

This matches a list's (<nil> for empty or <cons> for non-empty), recursively summing elements by applying the corresponding quotation if the pattern succeeds, with inverses defined in word properties for compile-time or memoized runtime evaluation. Such extensions create Lisp-like without native variables, leveraging the stack for binding. Similarly, syntax for loops can be extended using parsing words to define iterative constructs beyond basic combinators like each. For instance, a custom FOR: parsing word might scan a range and body quotation, suffixing an expansion that uses each internally:

SYNTAX: FOR: ... parse-until "do" [ ... ] parse-literal suffix! ;

SYNTAX: FOR: ... parse-until "do" [ ... ] parse-literal suffix! ;

This allows syntax like FOR: i 1..10 [ i sq . ], transforming it at parse time into a quotation applying each over the range with the squared index, demonstrating how parsing words build familiar control structures on Factor's foundational model. Infix-like notation can also be introduced, such as for ranges with ... parsing until the next token, expanding 12 ... 18 to [12,18) for concise sequence generation. These mechanisms, grounded in quotations, empower users to tailor syntax for specific domains while maintaining type safety through the stack checker.

Foreign Function Interface

Factor's Foreign Function Interface (FFI) enables seamless integration with code written in other languages, primarily by allowing Factor programs to call functions from dynamically linked libraries and vice versa. The FFI is implemented in the alien vocabulary and supports direct interaction with C libraries, with extensions for handling data types, pointers, and structs that align Factor's stack-based model with C's conventions. This interface is inspired by systems like Common Lisp's CFFI, emphasizing runtime loading and type-safe wrappers to minimize boilerplate. To load shared objects, Factor uses the add-library word or syntax extensions like LIBRARY: to dynamically link libraries such as .dll, .so, or .dylib files at runtime, supporting platform-specific paths and calling conventions like cdecl. Function wrappers are defined using FUNCTION: or the lower-level define-declared, which specify the return type, parameters, and ABI, automatically generating Factor words that handle stack-to-argument conversion. For example, to interface with a function int add(int a, int b), one defines:

FUNCTION: int add ( int a, int b ) ;

FUNCTION: int add ( int a, int b ) ;

This creates a Factor word add that pops two integers from the stack, calls the C function, and pushes the result. Calling conventions map Factor values to C types: integers and floats convert directly, while aggregates like structs use STRUCT: declarations for layout and passing by value or pointer. Pointers are handled via c-ptr types, with the >c-ptr word converting Factor objects like byte-arrays or structs to C pointers, enabling in-place modification. Support for is provided through the alien.fortran vocabulary, which adapts the FFI for Fortran's calling conventions and data passing, such as handling arrays and common blocks in shared libraries. Similarly, integration uses the cocoa or objc vocabularies, allowing Factor to invoke methods on Objective-C objects via runtime and , facilitating development for macOS and applications. Memory management across language boundaries requires caution due to Factor's moving garbage collector, which can relocate objects like byte-arrays passed as pointers. To avoid interference, developers use pinned or manually allocated memory via malloc and free words in the alien vocabulary, ensuring pointers remain valid during C execution or callbacks. Pinned objects, such as certain alien instances, are fixed in memory to prevent GC movement, providing safe buffers for long-lived C references. Callbacks from C to Factor are supported by registering quotations as function pointers, with the runtime handling invocation and stack setup. Embedding Factor in C applications involves linking the Factor VM as a library (e.g., libfactor.so) and using the embedding API defined in vm/master.h. Initialization occurs via init_factor_from_args, which loads an image file and processes arguments, after which Factor code can be evaluated from C using factor_eval_string to run expressions and retrieve string results. Conversely, C applications can be embedded in Factor by loading their libraries and invoking functions, as in the OpenSSL example where platform-specific libraries are loaded and functions like SSL_new are wrapped for secure socket operations. This bidirectional capability supports hybrid applications, such as games using SDL via FFI for graphics while leveraging Factor for logic.

Implementation Details

Compiler and Runtime Environment

Factor's implementation features a self-hosted optimizing written primarily in the language itself, augmented by a C++ (VM) that handles low-level operations such as primitive dispatch and . The supports to native , enabling high performance while preserving dynamic features like runtime code reloading. This architecture separates the high-level , implemented in Factor, from the base non-optimizing embedded in the C++ VM, which is used for rapid compilation during interactive sessions. The compilation process involves multiple stages beginning with and macro expansion, followed by optimizations such as checking with row polymorphism, quotation inlining, sparse conditional constant propagation (SCCP), , and . These optimizations operate on both high-level and low-level intermediate representations (IR), including static single assignment (SSA) form, to enable advanced transformations like value numbering, , and . The final code generation stage produces native binaries, with support for platform-specific features like SIMD instructions, ensuring efficient execution without relying on just-in-time () compilation. For interactive development, Factor employs a listener—a read-eval-print loop—that leverages the non-optimizing for quick evaluation of expressions, compiling them into executable quotations . This allows seamless experimentation while deferring full optimization to word-level compilation, where entire definitions are analyzed for better . The runtime environment, powered by the C++ VM, includes a generational garbage collector that uses a collector for the young generation and a mark-sweep-compactor for the old generation, facilitating automatic and supporting runtime code modifications through object compaction. Factor's fast startup is achieved via image saving, where the entire runtime state—including compiled and loaded vocabularies—is serialized to a file and reloaded instantly on launch. The bootstrap process begins with a minimal C++-generated base image containing the VM and essential primitives, which is then used to compile the full Factor system, including the and parser, culminating in a self-hosted development image. This partial self-hosting mirrors approaches in languages like , ensuring the core language can rebuild itself from source.

Standard Library Overview

Factor's standard library, known as the "basis," provides a rich collection of vocabularies that support a wide range of programming tasks, from basic data manipulation to advanced system interactions. These vocabularies are modular and extensible, allowing developers to load only what is needed for a given application. Key components include core utilities for data structures and , graphical interfaces, networking protocols, system-level operations, and specialized tools for databases, , and scientific computation. Core vocabularies handle fundamental data structures and utilities. The sequences vocabulary offers operations on ordered collections like lists and arrays, including mapping, filtering, and slicing functions. Associative data structures are managed by the assocs protocol, implemented in vocabularies such as hashtables for efficient key-value storage with constant-time lookups and vectors for dynamic, growable arrays. Utility vocabularies like strings provide string manipulation, formatting, and encoding/decoding routines, while math delivers arithmetic operations, , and basic statistical tools. For user interfaces and graphics, Factor includes a comprehensive GUI toolkit in the ui vocabulary, supporting windows, gadgets (such as buttons, sliders, and text fields), event handling, and layout management. Graphics capabilities extend to opengl for 3D rendering and hardware-accelerated visuals, and cairo for 2D vector graphics, enabling cross-platform drawing of shapes, paths, and text. Networking features encompass protocols and data exchange formats. The http vocabulary implements both client and server functionality, including request/response handling and status code management. Support for data serialization includes json for parsing and generating JSON structures, and xml for XML document manipulation. Lower-level connectivity is provided by sockets for TCP/UDP communication and the io streams for binary and text I/O over networks. System-level operations cover file handling, concurrency, and platform interactions. The io.files sub-vocabulary manages file reading, writing, and directory traversal, with support for paths and permissions. Concurrency primitives include the threads vocabulary for and channels for between threads. The system vocabulary queries OS details like CPU and usage, while io.launcher enables spawning and access. Specialized vocabularies address domain-specific needs. Database support features the db abstraction for relational queries and the binding for lightweight, embedded SQL databases. Cryptography is handled by the crypto suite, offering hashing (e.g., SHA-2 via checksums.sha), symmetric/asymmetric , and digital signatures, with bindings to for advanced features. Scientific computing basics are available in math extensions like blas for linear algebra operations, including and vector computations.

Community and Adoption

Development Status

Factor continues to receive active maintenance through its primary repository, where a small but dedicated of 145 contributors collaborates on enhancements and bug fixes. The project remains open to new involvement, with ongoing discussions and support available via a dedicated server. Recent blog posts on the official Re: Factor site, such as examples of parsing chemical formulas in October 2025, demonstrate sustained community engagement and practical exploration of the language's capabilities. The latest stable release, version 0.100, was issued on September 12, 2024, focusing on improved compatibility with modern operating systems—including an upgrade to 15.1, initial ARM64 non-optimizing compiler support, and automatic light/dark theme detection on Windows—alongside minor performance optimizations and support for compressed boot images. No subsequent major releases have occurred as of November 2025, though the codebase supports cross-platform deployment on , macOS, Windows, and . Comprehensive is hosted at docs.factorcode.org, featuring a guided tour that serves as an interactive for newcomers, covering core concepts like stack-based programming and combinators, as well as a detailed reference through the vocabulary index, which catalogs over 200 libraries and tools. Factor provides essential development tools, including the listener—a read-eval-print loop (REPL) for immediate code execution and experimentation—and integrations with popular editors such as Vim and for and interactive debugging. Deployment is facilitated through boot images, which bundle the runtime, , and custom code into standalone executables or saved sessions for easy distribution and resumption. While Factor's innovative design has garnered appreciation for niche applications, its adoption remains limited relative to mainstream languages, with usage concentrated among developers exploring concatenative paradigms for tasks like scripting, data processing, and prototyping where stack-based expressiveness shines.

Notable Applications and Usage

Factor originated as an embedded scripting language for game development, facilitating rapid prototyping and scripting in early game projects. The RogueBasin community has adopted Factor for roguelike game development, utilizing its built-in OpenGL-based graphics and GUI toolkit, as well as support for vector and matrix arithmetic essential for pathfinding algorithms like A*. In scientific computing and , Factor's concatenative stack-based paradigm enables efficient manipulation of data sequences, making it suitable for scripting numerical computations and processing pipelines where operations can be composed postfix without explicit variable assignments. Factor includes a robust HTTP server framework that supports , request dispatching, and session management, allowing developers to build web services and applications. For instance, it has been used to implement dynamic web apps with persistence and templating, running on standard ports for serving content. The language's enables seamless embedding into larger C or C++ systems, such as for UI scripting in performance-critical applications, where Factor code can be evaluated directly from C via a simple that initializes the VM and yields control for interactive execution. Community-driven projects demonstrate Factor's versatility in creative domains, including tools for music generation via support and image processing using its graphics vocabularies for manipulation and rendering. The GUI library provides a foundation for educational tools that teach concatenative programming concepts interactively.

References

Add your contribution
Related Hubs
User Avatar
No comments yet.