Factor Introduction

by "Blag" - Senior Developer Evangelist

Return to Geeky Thursday

What is Factor?


A stack-oriented programming language.


Dynamically typed with automatic memory management.


Included a large standard library.


Influenced by Forth and Lisp.

How to install Factor?


Simply go to Factor Main Page (http://factorcode.org) download a binary and extract it


On Linux run ./factor


On Windows run factor.exe or .\factor.com


on MacOS run Factor.app


Who uses Factor?


Sadly...not many people -:(


Stack-oriented programming is not exactly a hot topic these days...


While Factor is awesome...it's fanbase is rather small...


Starting out...


Factor is pretty much executed in a REPL


As soon as you open Factor, you will presented with the Listener


Basic Concepts


Comments start with an "!" but they must be followed by a whitespace...


					
! This a comment in Factor
					 
				

In Factor, we manipulate the stack...

and LIFO is used...(First In, Last Out)


					
1 2 3 4 5
				

we can print from the stack using a "." or ".s" (lowercase)


					
. . . . .
				
					
5 4 3 2 1
				

Keep in mind that ".s" doesn't clear the stack, while "." does it...


Basic Arithmetics

In Factor...we use the Polish Reversed Notation...


5 4 + .
9

5 4 + 2 - .
7

Stack Manipulation


Working with the stack it's not easy...that's why we have some nice and useful functions...

5 dup * .  ! duplicates the top of the stack
25

3 8 swap - . ! swaps the top with the second item
5
1 2 3 rot . . . ! rotates the top 2 elements
1 3 2

3 5 drop 2 + . ! removes the top item
5

1 2 3 nip .s ! removes the second item
1 3

Advanced Stack Manipulation


1 2 3 pick .s ! duplicates the last item into the
              ! top slot )
1 2 3 1

1 2 3 over .s ! duplicates the second item into the
                --top slot )
1 2 3 2

Seeing double


This operations handle the stack in pairs...actually they even go up to 4X

1 2 3 2dup
1 2 3 2 3

1 2 3 4 2nip
1 4
1 2 3 2over
1 2 3 2 1

1 2 3 4 2drop
1 2

Variables


In Factor, everything should be kept on the stack...so variables are not exactly the way to go...this is how you create a global variable...

SYMBOL: name
"Blag" name set-global
name get-global .

"Blag"

Words


Words are like functions...and they help us to not only organize but to control execution...

! This word will read two elements from the
! stack (x and y) and will return the sum (z)	
: sum_nums ( x y -- z ) + ;  

! This word will read two elements from the
! stack (x and y) and will return the sum (z)
! taking from the stack
: sum_nums ( x y -- ) + . ;  

4 5 sum_nums

9

1 2 3 sum_nums

--- Data stack:
1
5

Quotations


Something really cool about Factors are "Quotations" which basically are pieces of code pushed on the stack...

[ 2 + ]

4 swap call .

6

Lists


When we create lists, they go to the stack as one element...

{ 1 2 3 }

--- Data stack:
{ 1 2 3 }

We can access each element of the list, but...we loose the other values...


{ 1 2 3 }

0 swap nth .

1

--- Data stack:

We could simply fix it by adding a "dup" before the "0" -;)


Or we can access all elements...


{ 1 2 3 } [ 2 * . ] each

2
4
6

{ 1 2 3 } [ 2 * ] map

--- Data stack:
{ 2 4 6 }

Conditionals

The only conditionals are IF...and CASE...although CASE doesn't make too much sense for me...

25

20 > [ "Too big!" print ] [ "Too small!" print ] if

"Too big!"

Loops

Loops will repeat a command...


3 [ "Factor!" print ] times

Factor!
Factor!
Factor!

Here's a more complicate and naive way of doing a loop...


0 [ dup 3 < ] [ 1 + dup "Factor!" print ] produce 2drop

Factor!
Factor!
Factor!

While

Yep...we can use While as well...


0 [ dup 3 < ] [ 1 + "Factor!" print ] while drop

Factor!
Factor!
Factor!

Fibonacci List

We're going to create our first Factor application -:) A Fibonacci List

Load up the Listener and type the following...

USE: tools.scaffold

"fibo" scaffold-work  ! You might get an error saying 
                      ! that "fibo" doesn't exist...
                      ! ignore that -:)

This will create a "fibo" folder inside the "work" folder inside your Factor installation folder -;)

Edit the "fibo.factor" file that was generated...

! Copyright (C) 2018 Blag.
! See http://factorcode.org/license.txt for BSD license.
USING: math prettyprint kernel io math.parser command-line 
       namespaces sequences ;
IN: fibo

<PRIVATE

: ask-number ( -- ) "Enter a number: " print ;

: read-number ( -- n ) readln string>number ;

: list_fibo ( x -- )
 1 0
 pick 1 + [ dup . over over + rot drop ] times 3drop ;

PRIVATE>
: fibo ( -- ) ask-number read-number list_fibo ;

: fibo-run ( -- ) fibo ;

MAIN: fibo-run		

Go to the listener and simply type

"fibo" run


Our app is going to load and we should enter a number...



Let's analize the code a little bit...because it might be hard to understand...


"ask-number" simply asks for a number

"read-number" read the number and turn it into a integer

"list_fibo" Let's explain this in parts...using 5 as input...

5 1 0 pick 1 +

--- Data stack:
5
1
0
6 ! pick grabs 5 and then adds 1

6 will used as a parameter for the loop that follows...meaning, it will be executed 6 times...and of course this 6 will dissapear from the stack...

dup . over over + rot drop

Here we duplicate and print the first value from the stack. "Over" grabs the middle value and duplicates it, and we call it twice to then sum the values. "rot" will rotate the top two elements and finally "drop" get rids of the last value...


times 3drop

"times" executes the loop and 3drop gets rid of the last 3 values that remain on the stack...

Making an LED Number App


This one really took me a while...I even need to contact Factor's author -:)

Use "led_numbers" scaffold-work


! Copyright (C) 2018 Blag.
! See http://factorcode.org/license.txt for BSD license.
USING: prettyprint kernel io math math.functions 
       sequences generalizations math.parser 
       command-line namespaces ;
IN: led_numbers
<PRIVATE

: first_line ( x -- )
  dup 0 = [ " _  " write ] [ ] if dup 1 = [ "  " write ] [ ] if
  dup 2 = [ " _  " write ] [ ] if dup 3 = [ "_  " write ] [ ] if
  dup 4 = [ "    " write ] [ ] if dup 5 = [ " _  " write ] [ ] if 
  dup 6 = [ " _  " write ] [ ] if dup 7 = [ "_   " write ] [ ] if 
  dup 8 = [ " _  " write ] [ ] if dup 9 = [ " _  " write ] [ ] if 
  drop ;

: second_line ( x -- )
  dup 0 = [ "| | " write ] [ ] if dup 1 = [ "| " write ] [ ] if
  dup 2 = [ " _| " write ] [ ] if dup 3 = [ "_| " write ] [ ] if
  dup 4 = [ "|_| " write ] [ ] if dup 5 = [ "|_  " write ] [ ] if 
  dup 6 = [ "|_  " write ] [ ] if dup 7 = [ " |  " write ] [ ] if 
  dup 8 = [ "|_| " write ] [ ] if dup 9 = [ "|_| " write ] [ ] if
  drop ;
: third_line ( x -- )
  dup 0 = [ "|_| " write ] [ ] if dup 1 = [ "| " write ] [ ] if
  dup 2 = [ "|_  " write ] [ ] if dup 3 = [ "_| " write ] [ ] if
  dup 4 = [ "  | " write ] [ ] if dup 5 = [ " _| " write ] [ ] if 
  dup 6 = [ "|_| " write ] [ ] if dup 7 = [ " |  " write ] [ ] if 
  dup 8 = [ "|_| " write ] [ ] if dup 9 = [ " _| " write ] [ ] if 
  drop ;
  
: numdigits ( x -- x ) log10 1 + 1 /i ;

: split ( x -- x ) dup numdigits [ 10 /mod ] replicate swap drop ;

: lines ( x x x -- ) [ first_line ] each "" print 
                     [ second_line ] each "" print 
                     [ third_line ] each "\n" print ;  
: leds ( x -- ) split reverse dup dup lines ;

: ask-number ( -- ) "Enter a number: " print ;

: read-number ( -- n ) readln string>number ;

PRIVATE>

: led_numbers-run ( -- ) ask-number read-number leds ;

MAIN: led_numbers-run

When we run it we're going to see...


Decimal to Romans


This App will create a Roman Numeral based on a decimal number


Is not so hard, although it took a me while -:P


USE "romans" SCAFFOLD-WORK


! Copyright (C) 2019 Blag.

! See http://factorcode.org/license.txt for BSD license.

USING: math prettyprint kernel io math.parser 

       command-line namespaces sequences ;

IN: romans
<PRIVATE

CONSTANT: roman_values { 1000 900 500 400 100 90 50 40 10 9 5 4 1 }

CONSTANT: roman_keys { "M" "CM" "D" "CD" "C" "XC" "L" "XL" "X" "IX" 
	
                       "V" "IV" "I" }

: ask-number ( -- ) "Enter a number: " print ;
: read-number ( -- n ) readln string>number ;
: get-values ( x -- x ) roman_values [ /mod swap ] map swap drop ;
: get-keys ( x -- ) roman_keys [ <repetition> concat ] 
                    2map "" concat-as . ;

PRIVATE>
: romans-run ( -- ) ask-number read-number get-values get-keys ;

MAIN: romans-run

When we run it we're going to see...


That's all


Factor is not your typical language...

So I don't expect anyone to build huge applications with it...

Factor is good because it forces you to think...

Contact Information


Blag --> blag@blagarts.com

@Blag on Twitter

Go back home...