Premature Non-Functional Programming the Root of All Evil?

This post by Wesner Moise reminded me of my own post a few weeks back. Basically, the observation is that the “functional programming” paradigm, in which variables are immutable once bound, really seems to have something going for it.

In functional programming, the context necessary to achieve a function is passed in and the results are returned. Anything that changes that isn’t explicitly returned is a “side-effect” and one strives to minimize such things (as a practical matter, things like IO are necessarily (?) implemented as side-effects). Strings in Java or the CLR are immutable (although in use you often have s = s.Trim() or what-have-you).

Contrast:

class OopFoo{
  int bar;
  void Func1(int i){
    bar = i * 2;
  }
  int Func2(){
    return bar * 3;
  }
}
vs.
class FunctionalFoo{
  int Func1(int i){
    return i * 2;
  }
  int Func2(int i){
    return i * 3;
  }
}

In OopFoo, the results of OopFoo.Func1(int) are stored in an instance variable bar and calls to OopFoo.Func2() use that instance variable to perform their calculation. FunctionalFoo on the other hand, uses no instance variables: the behavior of FunctionalFoo.Func2(int) is entirely dependent on the parameter.

Traditional OOP design principles don’t provide a lot of guidance on selecting between these two interfaces but tends to favor the OopFoo approach, especially if the amount of context starts to get large (that is, if there were 10 variables necessary to calculate Func2, you’d likely store them in as instance variables). On the other hand, once you start unit-testing, there’s an automatic tendency to start favoring the functional approach, since unit tests generally have to construct and pass in context anyway. On the other other hand, if Func1()‘s calculation is logically internal to the implementation of the class, you certainly don’t want it visible, returning semi-logical values to external users (for instance, a function that validates the city portion of an address, but not the whole address). (Incidentally, the lack of internal visibility in modules is one of the things you note in the code of journeyman programmers: they “get” public and private, but the benefits of internal and to some extent protected are less used.)

Werner’s hesitancy about functional programming relates to another issue: resource consumption. Allocating a new data structure everytime you manipulate the structure seems fraught with peril. But one thing we learn from optimization is that programmers are poor at guessing how resource consumption plays out in the whole context of a program. Further, in some situations one can use design patterns (notably, Flyweight) to help actual, as opposed to logical, resource consumption.

I can’t say that excessive resource consumption can always be refactored away, but more and more, I find myself developing behavior in a functional manner and refactoring towards a more stateful interface over time.