Tag Archives: Scala standard library

Functional code != Good code

There’s a dangerous trap to fall into: The belief that functional code is automatically good code. Hopefully not too many people would come out and actually claim this, but it seems to be an unstated common belief. It’s also utter bollocks.

Functional programming gives you tools for writing good code. Good functional code can be very good (good imperative code can be very good too! But that’s not the point of this post). Bad functional code can be just as bad as its imperative cousin.

Now, what post about functional programming would be complete without some trivial one liners? Let’s start by summing a collection of integer elements.

Imperatively:

   var x = 0;
   for (j <- myCollection) x += j;

Functionally:

  val x = myCollection.foldLeft(0)(_+_);

The functional code is a little shorter. You’ve avoided making the sum variable mutable, which is nice. It introduces one fewer local variable which helps with reading the code (but it wouldn’t if Scala didn’t have the cut notation for anonymous functions, you’d have had to write (j, i) => j + i, which introduces *more* variables). Is the fact that you’ve used a higher order function an improvement? No, not really. The loop is a higher order function too. We could have written it:

   var x = 0;
   myCollection.foreach(x += _);

and it would have been exactly the same thing (literally. The Scala compiler emits something nearly identical to the second version when given the first version). So the only difference here is the lack of mutability. Both examples are trivial, so there isn’t a whole load of difference. But most code is just a sequence of trivialities, so considering how they’re written is valuable.

One advantage of the fold here is that you can use it as an expression without binding it to a variable. The loop doesn’t let you do that. But is that really a good thing for you to be doing?

Thing is, both of these examples are the wrong way to do it in the middle of other code. The right way to do it?

  val x = sum(myCollection);

(Unfortunately there is no sum method in the Scala standard library. A minor wart. But there’s one in e.g. scalax, or your personal utility classes, or wherever you care to look really).

Inline calculations like this should be factored out into simple methods. This is a no brainer, everyone knows it’s a good idea. And the fact that you’ve written your code in a single expression with a fold doesn’t exempt you from that.

Let’s make our trivial example very slightly less trivial. Instead of a collection of integers, I have a collection of collections. I want to find the total length.

Imperative version:

  var x = 0;
  for (c <- myCollection) x += c.size;

Now we’ll see how the functional code really shines in this example:

Functional version:

  val x = myCollection.foldLeft(0)((y, c) => y + c.size)

See how much better abstracted that is than the imperative version?

Um, no. Me neither.

When you’re doing folding like this if you don’t have an already defined function to be reusing (which doesn’t have to be a named constant, it could be the result of some other computation. It probably shouldn’t be a lambda), this really isn’t any better than imperative version. At least to my eyes I find it a bit worse, but that’s largely a matter of opinion. It’s sure as hell not better.

So how *should* you be writing it? Am I now going to say you should define a method sumLengthsOfCollections?

Fuck no.

You should solve it, as with so many other things, by reducing it to a problem you’ve already solved.

   val x = sum(myCollection.map(_.size));

Here you actually *have* reused logic properly, and the code is at least somewhat better. And if you wanted to later change the implementations of sum (Think this is a ludicrous comment? Really? What if this were acting on BigInts rather than integers. Still think it is?).

In these trivial examples it doesn’t make much of a difference, but as the number of variations and steps in the pipeline increases this style of code really starts to make a difference. Earlier today I had to deal with a method definition that involved a really large number of folds, most of them doing trivial things. I was not amused. The fact that you can fold, doesn’t mean that you should, and the fact that you’ve been given higher order functions isn’t a license to use the wrong ones and assume your code will be good anyway.

SBinary progress

If things have seemed a little quiet on the SBinary front, do not despair! It’s not because I’ve abandoned it. Partly I’ve been very busy recently, but I’ve also been held up with various issues on the implementation. One was waiting on Scala 2.7.1 as it fixes an issue I had with implicits, and another was a feature that I’ve decided to defer to 0.3 (to do with modifiers for binary instances. In particular I wanted to get sharing based on identity working properly, but I kept running into issues)

Anyway, I’ve spent most of today working on it and things are going pretty well. You can expect a 0.2 release at some point after 2.7.1 goes final. It will feature:

  • A revised API that I think is nicer to work with. It replaces the use of DataInput and DataOutput with custom Input and Output types. These define read and write methods for reading and writing things with Binary instances, plus a few other useful methods.
  • Improved generic methods for defining binary instances. In particular the length encoding has got a revamp and asUnionN has become significantly less irritating to work with.
  • A certain amount of experimental support for lazy IO via Streams. I’m not totally convinced this is a good idea, but it’s sufficiently useful that I’m going to provide it anyway with a big red warning sticker.
  • A much larger set of data types handled out of the box. It should cover most of the types available form the Scala standard library that it reasonably can (it can’t handle things like functions, etc)

As promised, this release should still be binary compatible with the last one.

This entry was posted in programming and tagged , , , on by .