Charlie Harvey

Seven More Languages: Factor Day One

A little interlude since third day of Lua. Today I was playing with Factor, a stack orientated, concatenative programming language with a postfix syntax. You heard me right, Factor’ uses Reverse Polish Notation. Interesting idea.

In some ways, I can see how useful being able to compose functions that way is, and the chapter author, Fred Daoud also mentions the benefit of this way of arranging the code. He gives the following example in Javascript h(g(f(42))); and contrasts it with Factor 42 f g h

That is a lot cleaner and less noisy.

It also reminded me of the way that I might compose pipes when working in the bash shell. Like this ls -alh | grep '[0-9][M]' | cut -d' ' -f6-12 | sort -rn | head -n3 That is a hacky way to find the 3 largest files (less than a gigabyte) in a directory, if you are interested.

My experiences

Installing factor on Debian Jessie was a bit of a faff to be honest. It seems that it expects libraries to end in .so, whereas Debian ends them .so.1 or .so.2 and so on. To get the Listener running I just put a bunch of symlinks in place. Naughty, I know. sudo ln -s /usr/lib/x86_64-linux-gnu/ /usr/lib/x86_64-linux-gnu/ sudo ln -s /usr/lib/ /usr/lib/ sudo ln -s /usr/lib/x86_64-linux-gnu/ /usr/lib/x86_64-linux-gnu/ sudo ln -s /usr/lib/x86_64-linux-gnu/ /usr/lib/x86_64-linux-gnu/ sudo ln -s /usr/lib/x86_64-linux-gnu/ /usr/lib/x86_64-linux-gnu/ sudo ln -s /usr/lib/x86_64-linux-gnu/ /usr/lib/x86_64-linux-gnu/ sudo ln -s /usr/lib/x86_64-linux-gnu/ /usr/lib/x86_64-linux-gnu/ sudo ln -s /usr/lib/ /usr/lib/ listener gui

Rather than a normal REPL, Factor comes with a deveopment environment called the Listener. Which sounds sort of Orwellian.

It is fine, but the GUI is not really the sort of thing I like to work with. I played around a bit to make it more readline like (i.e. using the up arrow to go back in my history rather than CTRL-P). That made things better for me.

First I installed rlwrap $ sudo aptitude install rlwrap Then I called the Listener in commandline mode from rlwrap $ rlwrap ./factor -run=listenerThat was a bit nicer.

Once I had got my environment set up, using Factor was actually really good fun. I enjoyed the crazy back to front syntax and the simplicity of composing functions to pop and fiddle with the stack. The exercises werr well explained and reinforced the content of the chapter well, I thought.


Easy exercises

Using only * and + how would you calculate 3^2 + 4^2

Assuming that I am allowed to use integers too ;-) 3 3 * 4 4 * + .

Enter USE: math.functions in the Listener. Now, with sq and sqrt calculate the square root of 3^2 + 4^2

3 sq 4 sq + sqrt .

If you had the numbers 1 2 on the stack, what code could you use to end up with 1 1 2 on the stack

1 swap

Enter USE: ascii in the Listener. Put your name on the stack and write a line of code that puts "Hello, " in front of your name and converts the whole string to uppercase. Use the append word to concatenate two strings and >upper to convert to uppercase. Did you have to do any stack shuffling to get the desired result?

Yep, I played around for a bit but I couldn’t think of a way to do it without shuffling the stack.

"Hello, " swap append >upper .

Medium exercises

The reduce word takes a sequence, an initial value, and a quotation and returns the result of applying the quotation to the initial value and the first element of the sequence, then the result of applying the quotation to the result of the next element of the sequence, and so on. Using reduce, write a line of code that returns the sum of the numbers 1, 4, 17, 9, 11. Try it on your own first, but if you are truly stuck, look back carefully over the pages you've just read. There is a hint hiding somewhere.

Fine, this is just a fold but with the syntax all backwards.

[ 1 4 17 9 11 ] 0 [ + ] reduce .

Now calculate the sum of the numbers 1 to 100 in a similar fashion. Do not manually write the sequence of numbers. Instead, enter USE:math.ranges in the Listener, and use the [1,b] word to produce the sequence.

USE: math.ranges 100 [1,b] 0 [ + ] reduce .

The map word takes a sequence and a quotation, and returns a sequence of results of applying the quotation to each value. Using map and the words you have learned so far, write a line of code that returns the squares of the numbers 1 to 10.

Great to see that Factor has map too.

10 [1,b] [ sq ] map .

Hard exercises

Write a line of code that, given a number between 1 and 99, returns the two digits in the number. That is, given 42 , you should get 4 and 2 on the stack. Use the words /i, mod, and b to accomplish this task.

I wasn’t completely happy with the repeated 10 here. I am sure I could have done some stack trickery to avoid repeating myself.

42 [ 10 /i ] [ 10 mod ] bi

Repeat the previous exercise for any number of digits. Use a different strategy, though: first convert the number to a string, then iterate over each character, converting each character back to a string and then to a number. Enter USE: math.parser in the Listener and use number>strng, string>number, 1 string and each.

This is a nice way to show how trivial function composition becomes when writing in RPN style. I was impressed.

942 number>string [ 1string string>number ] each


  • Be respectful. You may want to read the comment guidelines before posting.
  • You can use Markdown syntax to format your comments. You can only use level 5 and 6 headings.
  • You can add class="your language" to code blocks to help highlight.js highlight them correctly.

Privacy note: This form will forward your IP address, user agent and referrer to the Akismet, StopForumSpam and Botscout spam filtering services. I don’t log these details. Those services will. I do log everything you type into the form. Full privacy statement.