Monday, February 13, 2012


James O'Beirne wrote a great blog post on why languages matter with some thoughts on predictability, readability, and compactness. In it, he compares some examples of code in dynamic languages such as PHP, Python, and Groovy.

I wanted to compare his simple "readability" examples with Factor, to show why concatenative programming matters.


This example of some PHP code:

$x = 1;
$nums = array(10, 20, 30, 40);
$res = 0;

foreach ($nums as $n)
  if ($n > 15)
    $res -= $n*2 + $x;


He compares the PHP code favorably to this Groovy code:

def x = 1
def nums = [10, 20, 30, 40]
def res = nums.findAll { it > 15 } 
    .collect { it * 2 + x } 
    .inject(0) {accum, val -> accum - val}


Although James doesn't show a Python example, it could look something like this:

x = 1
nums = [10,20,30,40]
res = 0
for n in nums:
    if n > 15:
        res -= (n*2) + x

We could improve this by using a generator expression (or, equivalently, a list comprehension)):

>>> def foo(x, nums):
...     return -sum(n*2+x for n in nums if n > 15)

>>> foo(1, [10,20,30,40])


I would argue that a Factor version is pretty readable:

IN: scratchpad { 10 20 30 40 } [ 15 > ] filter
               [ 2 * 1 + ] map sum neg .

If you wanted to factor (ahem) this into a reusable word:

: foo ( x nums -- res )
    [ 15 > ] filter [ 2 * + ] with map sum neg ;

IN: scratchpad 1 { 10 20 30 40 } foo .


James O'Beirne said...

Awesome! I don't know anything about Factor, but I found your examples very readable.

mrjbq7 said...

Here's a comment that Abram Hindle emailed to me, and I wanted to share on his behalf:

Based on the models presented here

and using an extreme simplification of the model, I think for this example you can legitimately make the claim of factor being simple + readable.

Essentially information (entropy) negatively correlated with readability.

PHP entropy 4.47 bits per byte for 116 bytes

groovy entropy 4.47 bits per byte for 146 bytes

python 4.01 bits per byte for 90 bytes

python 4.12 for 130 bytes

factor 3.71 bits per byte for 105 bytes or 4.17 bits per byte for 134 bytes (second example)

So in factor was among the lowest entropy and simplest.

This might provide some kind of evidence that factor might be indeed readable. Although our models were tuned to Java and the human readability rankings were tuned to Java.


J.V. Toups said...
This comment has been removed by the author.
J.V. Toups said...

What do you suppose it is about Factor that makes the code more readable? It seems to me that factor forces you to consider very carefully the question of data flow. The factor concatenative data flow model is very simple and therefore restrictive, but what you get in exchange is that in the written form of a program, all data flow is implicit.

This is almost certainly the reason for the lower entropy of the factor code compared to the other dynamic languages - but we don't get this lower entropy for free. We pay for it during software construction, as anyone who has tried to learn the discipline of writing stack code has no doubt experienced. It can take some time to become comfortable with using the stack effectively.

This all reminds me of arrows from the world of pure functional programming. Arrows are an abstraction that limits the programmer to the expression of only a subset of dataflow manipulations. You must learn to express yourself in the limited vocabulary of arrows to represent stream manipulations. What you get in exchange is the assurance that your stream functions maintain certain properties (eg, no time leaks).

I'd love to see someone pursue these analogies a bit further - it seems the analogy between arrows and stack languages is pretty tight, but I am not sure.

megapoliss said...

Groovy version can be improved:

def foo(x, nums) {
x - nums.findAll{ it > 15 }.collect{it * 2 + 1}.sum()

foo 0, [10, 20, 30, 40]