Factor at a Glance — A Language a Day, Advent Calendar 2019 Day 24/24

About this ‘A Language a Day’ Advent Calendar 2019

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

$ export PATH=$PATH:/Volumes/factor/factor/

Having this, run a Factor program as:

$ factor helloworld.factor

Hello, World!

Here is the minimum program in Factor:

USE: io

"Hello, World!" print

The program puts "Hello, World!" on stack and applies the print word to it. To be able to use print, you need to load the vocabulary io first.

Stack

In the above Factor program, we can see words and literals. A literal, "Hello, World!", puts itself on the stack. A word, print, makes some action that takes the top object from the stack and prints it.

For example, push two strings to the stack and call print twice:

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 print word then takes it from the stack and prints. 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

Although the 2 is on the top of the stack, it is the second operand for the - operator.

Comments in Factor are started with !.

Variables

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 [let] construct.

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
]

Defining words

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 "John" literal.

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

The 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

Object-oriented programming

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

The accessors vocabulary creates the special words, >>name and name>> for setting and getting the fields of the person tuple.

First, 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 name there.

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

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.

Get more

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!

One thought on “Factor at a Glance — A Language a Day, Advent Calendar 2019 Day 24/24”

Leave a Reply

Your email address will not be published. Required fields are marked *