- Appeared in:
- Influenced by:
- Typing discipline:
- File extensions:
- Versions and implementations (Collapse all | Expand all):
Factor is a modern concatenative stack-based language that supports object-orientated and functional programming.
The language was created by Slava Pestov in 2003 (JFactor) as a scripting language for a video game. Since then the language changed a lot — most of the features were added as soon as their necessity became evident.
Factor is a concatenative language: the function calls and arithmetic operators use postfix syntax instead of infix or prefix ones typical for most other languages. The code is a sequence of commands which transform input data into output ones; this style is called pipeline code, since it resembles the use of pipes in Unix shells.
Factor is a stack-based language: stack is used to pass parameters to and from functions (“words”). The parameters are pushed on the stack, and the function pops them as needed. The resulting values are returned via the stack as well. The language provides named variables, for example, local within the scope of one word, but most language constructs don’t require them. Input and output parameters of all words must be described in a formal way using stack effect notation (see examples).
Factor provides higher-order functions called combinators and anonymous functions called quotations. Quotations consist of several words enclosed in square brackets; they can be stored on the stack without being executed and passed as parameters to combinators. Some of the combinators can be used to replace shuffle words — words that change the order of the elements of the stack.
Main combinators (the arguments are listed in the order they have to be pushed on the stack):
ifperforms given actions depending on the value of a boolean value. Arguments: boolean value, then-quotation, else-quotation. The versions of the combinator are
for-eachloop in other languages: it iterates over the elements of the array and applies a quotation to each of them. Arguments: array, loop body-quotation.
reduceis a variation of
eachwhich allows to accumulate a value between iterations. Arguments: array, initial value of the accumulator, loop body-quotation. The quotation should take the current value of the accumulator and the current element of the array.
mapiterates over the array and collects the output values from each iteration into a new array. Arguments: the original array, the quotation which transforms elements.
filterfilters the elements of the array which satisfy a certain condition into a new array. Arguments: the original array, condition quotation.
cleaveaccepts a value and an array of quotations and applies each quotation to the value, resulting in an array of values. For this combinator and the next ones, there exist shorthand forms applicable when there are two or three quotations. They allow to avoid extra curly braces (array delimiting tokens). Shorthand forms for
spreadaccepts a series of values and an array of quotations (both have the same number of elements) and applies each quotation to the corresponding value, resulting in a series of values. Shorthand forms are
napplyaccepts a series of values, a quotation and a number and applies the quotation to each value (the number of values in the series is given by the number argument). Shorthand forms are
Factor separates the module system (source code organization) and types/classes system (data processing logic organization). Source code of Factor programs is organized as a system of nested modules called dictionaries (similar to Java packages), which correspond to file structure of the code.
Object system in Factor is based on generic words which can have multiple implementations based on the classes of their arguments. Factor is a purely object-oriented language in the sense that every value is an object, and basic operations are performed by calling methods.
Factor provides support for metaprogramming, macros system and functors, which allows expanding the language easily. Besides, Factor includes tools for low-level programming, interfaces to procedures in other languages, binary data processing words and scoped resource management.
Elements of syntax:
|Function definition||: <name> ( <input> -- <output> ) <body> ;|
|Function call with no parameters||<name>|
|If - then||<condition> [ <body> ] when|
|If - then - else||<condition> [ <if-branch> ] [ <else-branch> ] if|
Hello, World!:Example for versions Factor 0.94
The first line imports
io dictionary (
USE: io "Hello, World!" print
Factorial:Example for versions Factor 0.94
The first line lists the necessary dictionaries:
math (arithmetical operators) and
Next the definition of
factorial word follows which replaces an integer
n with its factorial on the top of the stack. To do this, it constructs an array of numbers from 0 to
n — 1 (word
iota) and folds it with
1 using quotation
[ 1 + * ] (increment and multiply) and combinator
The main program constructs a list of numbers from 0 to 16 (
iota again) and for each of them (combinator
each) applies the quotation which calculates the factorial and outputs the result in required format.
math.combinatorics contains word
factorial defined exactly like this.
USING: formatting kernel math sequences ; IN: factorial-example : factorial ( n -- n! ) iota 1 [ 1 + * ] reduce ; 17 iota [ dup factorial "%d! = %d\n" printf ] each
Fibonacci numbers:Example for versions Factor 0.94
This example shows recursive calculation of Fibonacci numbers.
fib calculates the n-th number: if the argument is not greater than 1, it stays on the stack as the return value, otherwise it is replaced with a sum of previous numbers. Word
bi is a short-hand version of
cleave combinator and allows to apply two quotations (in this case calls of
fib for smaller arguments) to the same element of the stack (
USING: formatting kernel math sequences ; IN: fibonacci-example : fib ( n -- fib(n) ) dup 1 > [ [ 1 - fib ] [ 2 - fib ] bi + ] when ; 16 iota [ 1 + fib "%d, " printf ] each "...\n" printf
Factorial:Example for versions Factor 0.94
This example uses a purely recursive approach to factorial calculation. Word
n! on the stack with a side effect: it prints all values of factorial from 0 to
if combinator is applied, the stack holds values of
over replace them with
n!; two latter values are used for printing, and the first one stays on the stack as a return value.
In the main part of the program we need to add
drop to remove
16! from the stack, so that the effect of the program on the stack is
( -- ).
USING: formatting kernel math ; IN: factorial-example : factorial ( n -- n! ) dup 0 = [ 1 ] [ dup dup 1 - factorial * ] if swap over "%d! = %d\n" printf ; 16 factorial drop
Quadratic equation:Example for versions Factor 0.94
quadratic-equation takes coefficients of the equation as input and prints the solution while returning nothing. Note that this word is declared using token
:: instead of
: used in most cases; this means that within it lexically scoped variables can be used, in this case parameters
c as well as local variables
sd bound by
:> operator. Such variables can be loaded on the stack using their names. Words and operators which process lexically scoped variables are available in dictionary
Factor provides a built-in data type for complex numbers; whenever the discriminant is negative, its square root will be of type
complex. In this case complex roots are printed using words
imaginary-part which extract corresponding parts of a number.
readln reads a string from input stream (till the end of line), and
math.parser dictionary) converts a string to a floating-point number.
USING: formatting io kernel locals math math.functions math.parser ; IN: quadratic-example :: quadratic-equation ( a b c -- ) a 0 = [ "Not a quadratic equation." printf ] [ b sq a c * 4 * - :> d b neg a 2 * / :> x0 d sqrt a 2 * / :> sd d 0 = [ x0 "x = %f\n" printf ] [ d 0 > [ x0 sd + x0 sd - "x1 = %f\nx2 = %f\n" printf ] [ x0 sd + [ real-part ] [ imaginary-part ] bi "x1 = (%f, %f)\n" printf x0 sd - [ real-part ] [ imaginary-part ] bi "x2 = (%f, %f)\n" printf ] if ] if ] if ; readln string>number readln string>number readln string>number quadratic-equation
CamelCase:Example for versions Factor 0.94
This example uses regular expressions. Word
regexp) splits a string into an array of strings separated by matches to the given regular expression. After this
map combinator applies word
unicode.case) to each element of the resulting array, converting them to title case. Finally,
sequences) concatenates the strings into one using “” as a separator.
USING: kernel io regexp sequences unicode.case ; readln R/ [^a-zA-Z]+/ re-split [ >title ] map "" join print
Factorial:Example for versions Factor 0.94
This example uses a built-in word
factorial defined in dictionary
USING: formatting kernel math.combinatorics sequences ; 17 iota [ dup factorial "%d! = %d\n" printf ] each