Thursday, November 24, 2011

Functional Programming

I've been working in the US as a programmer for over a decade, and during that time, I've worked with a huge variety of engineers and hackers alike.

If you asked most people what the difference was, I think many would have a hard time quantifying it well.  My mind goes back to my first semester at Southampton University and our functional programming course, which was taught in SML.  The simplest problem, how to reverse a list was a challenge in functional programming that many couldn't wrap their heads around at first.  The idea of calling a function recursively to perform what in a declarative language was a loop was quite foreign, even to those of us who had done quite a significant amount of programming in our youth.

Here's an example of a solution in Scala (yes, I know there's a built in call for this, it's an example okay?):

def myReverse(x : List[String]) : List[String] = (List()/:x)((x:List[String],y:String) => List(y) ::: x)

Some folks might write something like this in Java:

public List<T> reverse(List<T> input) {
  List<T> l = new ArrayList<T>();
  for (int a = 0; a<input.size(); a++) {
  return l;

Or maybe even:

public void reverse(List<T> input) {
  for (int a = 0; a<(input.size()/2); a++) {
    T x = input.get(a);
    input.set(input.size()-a-1, x);

modify the list in place, what a good idea eh?  Sometimes, but mostly not so much, and many can't tell you why.  (And yes, there are slightly more elegant ways in pure Java, but few use them).  As we add complexity to this problem, the code gets increasingly worse.

As we add other features that are really great if you know how to use them, simple things like list comprehensions and similar, the code starts getting more obscure looking, but better.

And there lies the problem.  As we right more "efficient" code, it requires more and more skill to manipulate.  At what point do you draw the line and decide you've gone from good code to obscure code?

I have a few simple rules to write code by, the first one is "put shit where it goes".  It's a simple expression for avoiding tangling and scattering, a concept I learned about much later.  Some mechanisms fundamentally break this rule.  One such mechanism is Aspect Oriented Programming.  It is a very very powerful tool, and in the right hands, amazing.  The problem is is that it is probably the very definition of obscure.  Without a good toolset, you'd have no idea that the code even existed or was being executed against one of your classes, particularly if it didn't show up in a stack trace because the problem was being precipitated as a knock-on effect.  It could take a long time, possible forever to track down a problem.

Functional programming is less so, and I think keeps to the "put shit where it goes" rule much better.  With things like list comprehensions and closures, you can write shorter code that goes directly where it should.  Having more collections immutable makes things more reliable as you're less likely to get a modification vs copy problem.  I'm not sure of the performance cost of doing it this way though, but I get the feeling that of all performance problems in software development today, copy versus modify-in-place is not the worst of them by far.  I think it makes code more readable and clearer.  I think it can significantly reduce development time as well, at least if you don't strive to write "perfect" code.

No comments:

Post a Comment