Factor

Appeared in:
2003
Influenced by:
Influenced:
Paradigm:
Typing discipline:
File extensions:
.factor
Versions and implementations (Collapse all | Expand all):
Programming language

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):

  • if performs given actions depending on the value of a boolean value. Arguments: boolean value, then-quotation, else-quotation. The versions of the combinator are when (no else branch) and unless (no then branch).
  • each corresponds to for-each loop in other languages: it iterates over the elements of the array and applies a quotation to each of them. Arguments: array, loop body-quotation.
  • reduce is a variation of each which 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.
  • map iterates over the array and collects the output values from each iteration into a new array. Arguments: the original array, the quotation which transforms elements.
  • filter filters the elements of the array which satisfy a certain condition into a new array. Arguments: the original array, condition quotation.
  • cleave accepts 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 cleave are bi and tri.
  • spread accepts 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 bi* and tri*.
  • napply accepts 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 bi@ and tri@.

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:

Inline comments !
Case-sensitivity yes
Deep equality =
Comparison < >
Function definition : <name> ( <input> -- <output> ) <body> ;
Function call <name>
Function call with no parameters <name>
If - then <condition> [ <body> ] when
If - then - else <condition> [ <if-branch> ] [ <else-branch> ] if

Factor logo
Factor logo

Examples:

Hello, World!:

Example for versions Factor 0.94

The first line imports io dictionary (print word). The second line pushes the message string on the stack and then calls print which prints the top element of the stack.

USE: io 

"Hello, World!" print

Factorial:

Example for versions Factor 0.94

The first line lists the necessary dictionaries: formatting (printf), kernel (dup), math (arithmetical operators) and sequences (iota).

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 reduce.

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.

Standard dictionary 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.

Word 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 (n).

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 factorial replaces n with n! on the stack with a side effect: it prints all values of factorial from 0 to n. After if combinator is applied, the stack holds values of n and n!. Words swap and over replace them with n!, n and 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

Word 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 a, b and c as well as local variables d, x0 and 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 locals.

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 real-part and imaginary-part which extract corresponding parts of a number.

readln reads a string from input stream (till the end of line), and string>number (from 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 re-split (from regexp) splits a string into an array of strings separated by matches to the given regular expression. After this map combinator applies word >title (from unicode.case) to each element of the resulting array, converting them to title case. Finally, join (from 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 math.combinatorics.

USING: formatting kernel math.combinatorics sequences ;

17 iota [ dup factorial "%d! = %d\n" printf ] each