Ring In 2013 with Functional Programming
Learning a new programming language every year may be excessive, but this year is special.
If you haven’t had the chance to do so before, now would be a great time to learn functional programming (FP) techniques, and to start applying them, possibly within the language you are already using.
Here I’d like to briefly go over why FP is a great programming paradigm, especially for today’s software problems, and how conveniently it can be applied within a wide variety of languages.
The case for functional programming
With FP you will get software that is
- more concise
- more reliable
- easier to maintain
- and more parallelizable (this is especially relevant now that multicore systems are everywhere).
Let’s see how FP can perform this feat!
Functional programming relies in part on higher order functions, meaning functions that have other functions as parameters or return values. This allows functions to represent more abstract and reusable concepts, such as traversals. This increased reuse contributes both to concision and reliability. Since higher order functions require developers to frequently pass functions as arguments, FP languages generally provide facilities to create anonymous functions very conveniently, in any scope.
Functional programming also deals with immutable data, allowing variables and fields to be initialized, but never reassigned. This restriction makes it easier to verify properties of programs, both for programmers and for compilers: when writing or reviewing a piece of code, you never have to worry about the potential for some other part of the code to concurrently modify the data you are working with. This greatly facilitates parallel computation and the greater isolation between parts of the code makes it more modular and favors reuse.
Without immutability, higher order functions would not be as useful because the caller would depend on the order in which the higher order function called its arguments functions (those could perform mutation in a way which is order sensitive).
Finally, FP languages typically have more capable type systems. These allows developers to create more precise types. Many invariants which were described in comments and supported by test cases, can now be encoded within types and proven by the compiler. By simply checking the correspondence of types, the compiler is in fact carrying out a proof that all executions of the program will respect important program properties. Typically FP languages also relieve you from having to write most type annotations as the compiler is able to perform type inference.
Such type based verification of programs can go very far. In fact it can prove full program correctness in languages which are capable of representing dependent types. However, those more precise types cannot generally be inferred automatically. Writing such proven programs represents more work and should therefore be reserved for highly critical code.
Type systems also have great benefits in non-functional languages, but the presence of mutable data limits their ability to prove program correctness. With immutable data there is no such limitation and skillful use of the type systems can produce extremely reliable software.
This type of compiler verified reliability greatly simplifies software maintenance, and even development itself. Changes can be made to source code with great confidence since the compiler is catching most errors. This enables even simpler and concise code since refactoring can be applied more liberally as program requirements evolve.
The reliability benefits of FP are in large part due to the compiler’s ability to detect very large classes of errors. This automatic verification also makes the software easy to maintain since most incorrect changes are identified during compilation. And of course having more concise (yet readable) code also helps with maintainability.
Functional languages and libraries
If the above seems convincing, you may still be unconvinced about the practical applicability of these techniques, either in functional or mainly object-oriented languages.
Are functional languages sufficiently mature?
Functional languages such as Scala, F#, OCaml and Haskell can now be used very effectively in demanding commercial applications thanks to more mature tools and libraries. This was clearly observable at the 2012 CUFP conference where a diverse selection of commercial users presented very positive experience reports. I invite you to judge for yourself by viewing some of the archived presentations.
Can these techniques be used in object-oriented languages?
Many of them can. The result may not be as syntactically elegant as in functional languages, so some of the conciseness gains may be reduced, but you can still gain a lot in terms of reliability and support for concurrency and parallelism.
In fact many non-functional languages have recently been revised to support anonymous functions and somewhat improved type inference:
- C# 3.0 (2007)
- C++11 (2011)
- Java 8 (due in 2013)
These developments are making functional techniques a lot more practical, and are supporting the development of libraries based on functional principles.
I have had positive experience with one such library called Functional Java.
Recently I was asked to help out, for a week or so, on a web project based on Java and Play. The task was to add custom charts to a generated PDF report. I picked JFreeChart as the charting library (which worked out pretty well), but it required me to pass in the data points as two separate arrays of the same length (one array per axis). I needed to do some processing on these points and this was not a comfortable representation. I wanted it to be a list of pairs so I could filter and transform the list conveniently. This was the perfect job for Functional Java.
So I used options, tuples, and lists, and performed filters, maps, folds and sorts to process the points. Then I converted to the Java arrays needed by JFreeCharts using a couple more map operations, and the array functionality of Functional Java.
Thanks to the library, I never had to write for loops, rely on mutation, or guess whether I had to test for null values. The approach eliminated a whole class of potential bugs and made the code conceptually simple. Syntactically, it wasn’t ideal, because of the lack of language-level lambda expressions in Java 6 – but almost all of the anonymous class boilerplate was written automatically by the Eclipse IDE. The inherent safety of the approach also made unit tests unnecessary in this case.
I was quite satisfied with the result, and things will get even better in Java 8.
Although FP can be used successfully in other languages, it is best to get acquainted with FP through a functional language. I would suggest Scala, F#, OCaml or Haskell. My own introduction was through the first edition of Introduction to Functional Programming, which was based on Miranda. The second edition of that book is based on Haskell and may be a good choice. I would recommend you select resources that focus on teaching functional programming rather than the specifics of the language, like Functional Programming in Scala, whose first sentence is “This is not a book about Scala.”
Learning functional programming has always been very rewarding in terms of programming skills. But if you learn it in 2013 you will have access to a wide selection of mature languages in which you can apply those skills productively.