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.
PHP
This example of some PHP code:
<?php $x = 1; $nums = array(10, 20, 30, 40); $res = 0; foreach ($nums as $n) if ($n > 15) $res -= $n*2 + $x;
Groovy
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}
Python
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]) -183
Factor
I would argue that a Factor version is pretty readable:
IN: scratchpad { 10 20 30 40 } [ 15 > ] filter [ 2 * 1 + ] map sum neg . -183
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 . -183
Awesome! I don't know anything about Factor, but I found your examples very readable.
ReplyDeleteHere's a comment that Abram Hindle emailed to me, and I wanted to share on his behalf:
ReplyDeleteBased on the models presented here
http://softwareprocess.es/static/Readability.html
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.
abram
This comment has been removed by the author.
ReplyDeleteWhat 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.
ReplyDeleteThis 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.
Groovy version can be improved:
ReplyDeletedef foo(x, nums) {
x - nums.findAll{ it > 15 }.collect{it * 2 + 1}.sum()
}
foo 0, [10, 20, 30, 40]
-183