Skip to main content

Know When to Fold

Posted by cayhorstmann on October 5, 2008 at 8:26 PM PDT

I am teaching an undergraduate href="http://horstmann.com/sjsu/cs152/schedule.html">course in programming
languages. We build interpreters and compilers for toy languages, in the
hope that students gain a basic understanding of syntax, semantics, and
language translation.

I use Scala to
implement the interpreters and compilers because of its nifty “combinator
parser” library. ( href="http://szeiger.de/blog/2008/07/27/formal-language-processing-in-scala-part-1/">This
and href="http://debasishg.blogspot.com/2008/04/external-dsls-made-easy-with-scala.html">this
blog have nice introductions into Scala combinator parsers.) Why not just lex
and yacc, the Model T of parser generators? My colleague is doing just that,
and it is a good thing because it gives students much-needed experience with C
programming. But I don't have the heart to see students suffer with pointer
errors. With Scala, we can implement an href="http://horstmann.com/sjsu/cs152/SL1.scala">interpreter for a simple
language that supports arithmetic, if/else, and closures, in < 150 lines
of code.

I am learning Scala on the job, and I don't know all of the language
features yet. In particular, surely there must be some kind of
while or for loop, but I haven't run into it yet.

The reason is that I try to follow the advice of the Scala gurus and use
val instead of var whenever I can. If you can't
update any variables, and generally try to avoid side effects, there isn't much
you can do in a loop body. (Experienced functional programmers will surely say
“Duh”, but it came as a surprise to me)

Instead,
I find that I use the “fold” operator an awful lot. Let me explain
the operator with the shopworn example of computing the product

1 * 2 *
... * n
. Here is the iterative solution, which uses an unsightly
var:

def fac(n : Int) = { var r = 1; for (i <- 2 to n) r *= i ; r }

As L. Peter Deutsch said, to iterate is human; to recurse, divine:

def fac(n : Int) : Int = if (n <= 1) 1 else n * fac(n - 1)

Recursing may be divine, but it even better to fold:

def fac(n : Int) = (1 /: (2 to n)) {_ * _}

To visualize what is going on, draw a tree:

      op
     /  \
    op   4
   /  \
  op   3
/  \
1    2

The /: operator has three arguments:

  • An initial value, the leftmost node of the tree; here 1
  • A list of values, the right fringe of the tree; here
    2 3
      4
  • A binary operation that is applied at each node; here the multiplication
    function {_ * _}

There is also a :\ if the tree happens to go the other way.

At first, I thought that these operators are mere curiosities, but they
turned out to come in handy many times. Consider evaluating a block in a toy
language:

{
   val a = 3;
   val b = 2 * a;
   val fun = { x => x * x };
   ...
}

Each definition has to be added to an environment (a mapping of names to
values). Here is the tree:

             op
           /    \
          op     third def.
        /    \
       op      second def.
     /    \
original   first def.
environ.  

What is the operation? To add the definition to the previous environment.
There is a function evalDef that takes an environment and a
definition, returning the environment that is augmented by the definition.
(That's what you do when working with immutable data structures.) To add all
definitions, you simply use

(env /: block.defs) { evalDef(_, _) }

src="http://weblogs.java.net/blog/cayhorstmann/archive/blue-collar.jpeg" />Ok,
what's the point? I am interested in blue collar languages because they get
lots of users, and when there are lots of users, someone builds great tools.
Many years ago, I switched from Pascal—the original href="http://www.pbm.com/~lindahl/real.programmers.html">quiche eater
language—to C so I could get decent tools and libraries.

With functional languages, there are all these nifty idioms that lead to
very concise code that looks like random line noise to the uninitiated. (It
isn't just Scala—in Ruby, /: is called inject,
and Ruby programmers are just as delighted when they discover it. Read href="http://www.xcombinator.com/2008/02/22/ruby-inject-and-the-mandelbrot-set/">this
blog about using inject for printing a Mandelbrot set, surely
an activity that only a quiche eater can love.) I can just hear the blue collar
programmers looking at (1 /: (2 to n)) {_ * _} and wailing
“oh my, what is this, my head hurts”.

The conventional wisdom (for example, expressed in this href="http://guidewiredevelopment.wordpress.com/2008/10/03/dont-believe-the-hype/">blog)
is that functional programming will never be mainstream. I used to think that
too, but now I am having second thoughts. After all, if you now look at Pascal,
it is hard to imagine why people were so opposed to it. It is just C with a
saner syntax, or Java with only static methods and no objects. And Microsoft,
surely the bastion of the blue-collar programmer, is adding all sorts of
functional programming features to its flagship language. Check out href="http://blogs.msdn.com/charlie/archive/2008/07/24/linq-farm-more-on-aggregate-operators.aspx">this
blog on folding with LINQ. It is pretty disconcerting—kind of like
Sarah Palin's correctly pronouncing nu-cle-ar.

Maybe we are at that point in history when closures are becoming
mainstream?

Comments

Hi, you also might be interested implementing fac with utilizing the 'Stream' class when you want more the just a single value: http://gestalt.monoid.net/blog/2007/10/streams-for-incremental-and-tail....

Hi, Folds are indeed very useful. Before anyone complains about weird symbols in Scala, one should note that there are non-symbolic alternatives for both variants represented here, they are unsurprisingly named foldLeft and foldRight. Regarding for/while loops, I am not sure if you were joking but Scala has while loops that behave like Java's and for/sequence comprehensions[1] that are more powerful than Java's for loop. [1] http://www.scala-lang.org/node/111