Welcome to Day 24 of this year’s A Language a Day Advent Calendar. Today’s topic is introduction to the Factor programming language.
Facts about the language
Some facts about the Factor programming language:
- A concatenative stack-based language
- Dynamically typed
- Appeared in 2003
- Website: factorcode.org
Installing and running Factor
You can compile Factor from sources or download an image for your operating system. This method is quite unusual, and it delivers you a mounted image with a binary file and all the rest inside. There is a visual IDE, Factor.app (for Mac), but let me use the command-line tool. For simplicity, you can export the
$ export PATH=$PATH:/Volumes/factor/factor/
Having this, run a Factor program as:
$ factor helloworld.factor
Here is the minimum program in Factor:
USE: io "Hello, World!" print
The program puts
"Hello, World!" on stack and applies the
In the above Factor program, we can see words and literals. A literal,
"Hello, World!", puts itself on the stack. A word,
For example, push two strings to the stack and call
USE: io "Hello," "World!" print print
This program prints the two lines with the two given strings:
$ factor printprint.factor World! Hello,
You should keep the same considerations in mind when dealing with arithmetics. This is how you print the sum of two numbers:
USING: io math present ; 40 2 + present print
The two numbers are put on stack, then the
+ word is applied, which takes the two values from stack, computes the sum and puts it back on the stack. The
present word is needed here to get a string representation of a
fixnum data type.
Be careful when the operation is not commutative:
40 2 - present print ! 38
2 is on the top of the stack, it is the second operand for the
Comments in Factor are started with
The concept of a stack and variables are kind of mutually-exclusive. Nevertheless, there exist lexical, dynamic, and global variables. For the sake of simplicity, let us only look at the lexical variables. We need to draw the scope, and you do it with the
USING: io present locals ; [let 42 :> answer answer present print ]
Note that there should be no space between the opening bracket and
let. The whole
[let is a single word and you can’t split it. Words are separated by spaces, and that is the reason why you need to put a space before the semicolon.
If you need more than a single variable, just list them all:
USING: io present locals math ; [let 100 :> a 200 :> b a b + present print ]
You can think of a word as of a function that takes its arguments on the stack and puts the result back. To define a new word, use the
:: syntax. You also need to declare the stack effect, i. e. the number of elements on the stack to be taken and to be placed.
USING: io locals sequences ; IN: myprogram :: greet ( name -- phrase ) "Hello, " name append "!" append ; "John" greet print
Here, we define the
greet word, and it has to be in a vocabulary. For this, the
IN: word is used; we’ll define the
myprogram vocabulary here.
The stack effect
( name -- phrase ) informs that the word takes one element from the stack and puts one element onto it. The word body concatenates a few strings and leaves it on the stack. Then, we can use the word and apply it to the
A Factorial example
Let us define the factorial as a word taking a value from the stack and putting the result back.
USING: io locals present math kernel ; IN: myprogram :: factorial ( n -- f ) n 2 < [ 1 ] [ n n 1 - factorial * ] if ; 1 factorial present print ! 1 5 factorial present print ! 120 7 factorial present print ! 5040
factorial function is recursive, and thus it needs a condition
n 2 < …
if to stop iterating. Notice how the product of n * (n – 1)! is spelled out:
n n 1 - factorial *. Alternatively, we could arrange the operands differently:
n 1 - factorial n *.
The Factor library already includes the implementation of the
factorial function, so it is possible to use it directly:
USING: io math.factorials present ; 1 factorial present print ! 1 5 factorial present print ! 120 7 factorial present print ! 5040
Classes in Factor are implemented via tuples. In the following example, a tuple definition is created and then a new object is created:
USING: io accessors kernel ; IN: people TUPLE: person name ; person new "John" >>name name>> print
accessors vocabulary creates the special words,
name>> for setting and getting the fields of the
person new creates a new object and puts it on the stack. Then, we set the name by applying
>>name to the two top stack values, which are the tuple itself and the
"John" string. At this moment,
person is still on the stack.
To print the name,
name>> is called, which removes the person from the stack and puts its
How to add another field to the class? The tuple definition should be changed to:
TUPLE: person name age ;
Setting the data is also straightforward:
person new "John" >>name 22 >>age
Reading two fields is a bit trickier. As getting the name via
name>> removes the object from the stack, you need to duplicate it first so that you have its copy (a reference) when you need to read its
dup name>> print age>> present print
And let me stop here with Factor. There is a lot more to learn, but I hope you already got some understanding of a stack-based language.
The main resources for the Factor language are its own documentation and source codes:
Also, the following external links may be useful:
The source code of the examples in this blog post are on GitHub.
And that’s the end of this year’s Advent Calendar! Thank you for reading!