Beginning Factor - Introduction

I’ve been involved in the Factor programming language community for about a year now, and am constantly amazed with how productive its contributors are. Large improvements to the language and its libraries are made on a weekly (if not daily) basis, and it’s finally starting to attract some much-deserved attention from the programming community.

The problem is, the language is a huge departure from the norm for most developers and it can be overwhelming to someone just getting started. I would like to help ease that transition by posting on various topics that I know have been confusing to me over the past year.

Note that many topics are covered in the official FAQ — which is well worth a read — and I won’t spend time covering how to install Factor on the 14 or so platforms it supports, but beyond that, I’ll try to give enough information (from basic to advanced) to get you going.

The Basics of the Basics

First of all, Factor is a stack-based language, which means that it uses a stack to store all arguments and returned values from functions (called “words” in Factor). To put that another way, Factor words don’t receive arguments in the traditional manner; all input values that your word needs are expected to already exist on the top of the stack.

What is a Stack?

If you’re unfamiliar with the data structure called a stack, the concept is fairly simple. It centers around the idea of “Last In First Out” (LIFO), meaning the last item placed onto the stack is the first item that will you will get when you remove an item from the stack. You cannot get to the lower items until the all of the items above it have been removed.

I like to picture a stack like a tower of LEGOs. The only thing you can do with it are add another brick to the top (“push” an item onto the stack), or take the top brick off to use it for something else (“pop” an item off the stack). That’s all there is to stacks!

Pushing an Item onto a Stack

How to Use the Stack

So, now we know that Factor manages its input and output with a stack, but how do we actually use it? Well, when entering data into Factor’s listener, one of two things is happening:

  1. You are pushing a literal onto the stack
    OR
  2. You are calling a word which will consume literals from the stack

Things do get slightly more complicated than that, but for the most part those two rules hold true. The most simple example of this behavior is the ubiquitous hello world program, shown entered directly into Factor’s interactive listener environment:

(scratchpad) "Hello world!" print
Hello world!

To understand what is happening, you can just type the string first, and then execute the print word later:

(scratchpad) "Hello world!"

--- Data stack:
"Hello world!"
(scratchpad) print
Hello world!

In this particular case, typing the literal string "Hello world!" will push that value onto the stack; then the word print, takes a single string off the stack and writes it to the output stream.

The same idea can be applied to simple arithmetic. Like strings, numbers are also literals, so you just have to type them in (separated by spaces) for them to be pushed onto the stack:

(scratchpad) 2 3

--- Data stack:
2
3

NOTE: the stack is displayed in the listener upside-down from the way you’d think, so the bottom number is actually the top of the stack.

… and if you want to add the top two numbers together, the + word will simply pop two numbers off the stack, add them together, and push the result back onto the stack:

--- Data stack:
2
3
(scratchpad) +

--- Data stack:
5

A Few More Words

If you start messing around in the listener, odds are your stack is going to grow pretty quickly and become unmanageable. There are a few words that are essential to know in order to keep things under control:

drop ( x -- )
removes the top item from the stack and discards it
. ( obj -- )
takes the top item from the stack and prettyprints it
clear ( -- )
removes all items from the stack
(scratchpad) 2 3 -

--- Data stack:
-1
(scratchpad) drop
(scratchpad) 20 5 / .
4
(scratchpad) 2 3 + 6 7

--- Data stack:
5
6
7
(scratchpad) clear
(scratchpad)

Ramifications of Stack-Based Design

From these simple examples, we can observe a couple of important things about Factor:

  1. Postfix Notation

    When using a stack, passing data becomes implicit and we can assume that all input needed by words already exists on the stack. This naturally lends itself to using postfix notation because you have to push your data onto the stack before you can use it.

    This also makes words more concise and unambiguous when compared to infix notation and eliminates the need for copious amounts of parentheses used by prefix notation languages, like Lisp or Scheme.

    POSTFIX:  6 5 4 * +
    INFIX:    6 + 5 * 4 =
    PREFIX:   (+ (* 4 5) 6)

    With the infix example above, you’d have to know order of operations rules in order to get the correct answer (or use parentheses to force the matter). When using postfix notation, the fact that multiplication is done first becomes explicit. Prefix notation also gets rid of that ambiguity, but becomes messier and harder to type with the more nesting you add.

  2. Calling Words is Implicit

    You don’t have to specify that you’re calling a word, you simply use the word. This, combined with postfix syntax, means that you can easily nest words or cut and paste parts of definitions into new words without disrupting the flow of data. This lends to keeping code modular, short, easily testable, and readable.

Your First Word

If you’ve typed in the examples from above and messed around in the listener, then you might want to know how to write your own word rather than just using ones that are predefined. Drawing on what we already know, here’s how to write your first word:

: plus-two ( x -- y )
    2 + ;

If you copy and paste that into your listener, than you can use the word plus-two anywhere you would like to add two to a number on the top of the stack:

(scratchpad) 15 plus-two .
17

Not very exciting, but it gives us a couple more things to talk about…

Syntax Specifics

If you study the word definition, you’ll see that it’s made up of a few elements:

: plus-two ( x -- y )
    2 + ;

  1. A colon (:) is used to start the definition of a word. This is required and must have a space after it.
  2. Right after the opening colon is the name of your word, also required.
  3. Following the name of your word is its stack effect declaration, which is a list of the word’s inputs and outputs separated by -- and surrounded by parentheses. All words must have a stack effect declaration unless it only pushes literals on the stack. The names of elements in the stack effect declaration don’t make a difference, only the number of elements. That means that ( elt elt -- seq ) and ( x y -- z ) are the same thing. There are some common conventions for these names, but don’t get caught up by it as I’ll talk more about them in a later article and it won’t change how your program runs.
  4. Next comes the word definition itself, in this case 2 +.
  5. And the last item is a semicolon (;), used to end the word definition. This is required and must have a space before it.

The reason that everything must be surrounded by spaces is that there aren’t any syntax-only elements to Factor…everything is a word! The colon/semicolon, the parentheses, etc. are all just parsing words working together in order to create the syntax.

Next Time

Next time we’ll talk more about stack shufflers, quotations & combinators, details about more datatypes, and more…

If something is unclear or if you’re having any trouble, let me know and I’ll try to help out!

13 Comments

  1. Posted December 1st, 2008 at 2:51am | Permalink

    Great blog, Aaron. Just what I’ve been looking for! Keep it up.

  2. Norman
    Posted December 1st, 2008 at 4:05am | Permalink

    This is a fine and very much needed article !! Please keep it up, there’s a clear need for articles at this level, the language documentation is excellent but quite hard for a beginner to use.

  3. Jon
    Posted December 1st, 2008 at 4:48am | Permalink

    Nice work! I’m looking forward to the next chapter. ;-)

  4. Jon
    Posted December 1st, 2008 at 5:18am | Permalink

    When you introduce new words, would it be possible in some way to indicate whether they are primitives or not, and if they’re not, supply a link to the definition of the word?
    If I’m correct, “drop” is a primitive, but “clear” is not. With “.” I’m not sure.

  5. Posted December 1st, 2008 at 7:30am | Permalink

    Right below the (x — y) notation 2 +; does not seem to reference x at all. Is x bound to anything or is there some default variable? Or is the word definition really just a macro sort of thing? Like 2 + is what will be “entered” and the effect on the stack will be to remove the top word and put one back as a result?

  6. Posted December 1st, 2008 at 8:52am | Permalink

    “If I’m correct, ‘drop’ is a primitive, but ‘clear’ is not. With ‘.’ I’m not sure.”
    – Jon

    That is correct, drop is a primitive, clear and . are not, but in all honesty that differentiation is not very important and the definitions of those words would probably be confusing for people just starting out. If you want to see the definition, you can type \ clear see in the listener (likewise for any other word). Clicking around in the listener and the documentation browser is a great way to explore the libraries and learn how everything fits together.

    “Is x bound to anything or is there some default variable?”
    – tonetheman

    In Factor, you don’t commonly have the need for variables since all of the data passing happens directly on the stack. The use of locals is supported, but is not often used unless it drastically cleans up a word’s definition. If you’re curious, you can type "locals" about in the listener to pull up its documentation.

    In this particular case, “x” is just a generic placeholder name used in the stack declaration effect to show that one item is expected to be on the stack before this word is run (there can be other items below it, but they won’t be effected), and the “y” shows that there will still be a single item on the stack after the word has been called. As mentioned in the syntax specifics section above, the name of “x” itself does not matter, only the number of items in the stack effect declaration.

  7. wolf550e
    Posted December 1st, 2008 at 8:54am | Permalink

    RE: tonetheman
    As far as I understood from Slava’s presentation at Google (http://www.youtube.com/watch?v=f_0QlhYlS8g), “2″ means “push integer 2 on the stack” and “+” means “pop two values off the stack, add them, push the result on the stack”. The x and y are not bound to anything, and the code is generated by the jit per each concrete type of values on the stack that occur at runtime.
    There is a syntax that allows to declare named variables, supposedly to ease direct porting of code originally written in other languages, but it’s just a library that compiles the code to exactly the same stack shuffles like any other factor code.
    There is a syntax to show definition of stuff like “+”.
    Is this really the first factor from scratch tutorial on the web? No wonder nobody knows about it!

    Author: good work, please continue.

  8. wolf550e
    Posted December 1st, 2008 at 8:58am | Permalink

    Is there a compile time check whether the word stack effects declaration matches the code? Like, if there is a control flow path that leaves the stack with a different number of values than declared, will I see an error before I execute the code?

  9. Posted December 1st, 2008 at 9:26am | Permalink

    “Is there a compile time check whether the word stack effects declaration matches the code?”
    – wolf550e

    Yes, there is a check…if what the compiler infers does not match the stack effect declaration, it will generate an error. If we had left off the “x” in our declaration of plus-two, the error generated would look something like this:

    In word: plus-two
    Stack effects of the word plus-two do not match.
    Inferred: (( object -- object ))
    Declared: (( -- y ))
    

    To read more about stack effect inference, type "inference" help into the listener…

  10. Jon
    Posted December 1st, 2008 at 3:44pm | Permalink

    Hi Aaron,
    You said “Yes, there is a check…if what the compiler infers does not match the stack effect declaration, it will generate an error.”
    I just tried that. I entered this:

    : plus-two ( -- y ) 2 + ;

    … and I got no warning. When I did “\ plus-two see”, I got the same thing back. And doing “4 plus-two” I got 6. Maybe there’s a better example of the compile time check of the stack effect declaration? Or am I missing something?

  11. Slava Pestov
    Posted December 1st, 2008 at 4:26pm | Permalink

    Jon: you won’t see inference errors for words you enter in the listener. Words entered in a source file will display compile errors when you load the source file.

    However, even for words defined in the listener, you can still do:

    [ plus-two ] infer.

    which in your case would display an error about the stack declaration mismatch.

  12. Posted December 1st, 2008 at 5:09pm | Permalink

    “Maybe there’s a better example of the compile time check of the stack effect declaration? Or am I missing something?”
    — Jon

    Yeah, sorry about that. When developing in Factor, it’s standard practice to use an external text editor, and hit F2 in the listener to reload your vocabulary (a collection of words in a file) whenever you make changes. I plan on discussing that process in more detail later, but forgot about the differences when pasting code directly in the listener.

    Just follow what Slava says…pretty much always :-)

  13. Posted December 4th, 2008 at 4:18pm | Permalink

    Thanks a lot for your Factor tutorials , that’s the only thing this language missed : some readable documentation.

Post a Comment

Your email address is never published nor shared. Required fields are marked *

*
*