If you’re an experienced programmer, you probably know that learning new languages can teach you new things about programming. The more a given language’s underlying assumptions about programming style differ from those of languages you already know, the more you stand to learn from it. In some cases, you may learn more about what to avoid by running up against the limitations of a poorly designed language, but almost every language has something positive to teach you.
Languages built on a functional programming paradigm are entirely absent in many development shops, and even in many developers’ careers. Learning even one good functional programming language can offer great rewards for the developer interested in improving his or her programming skills. One characteristic of the functional programming paradigm in particular can even help you write much more secure code on the first try.
The term “functional programming”, sometimes abbreviated “FP”, refers to a programming paradigm wherein computation is specified by defining functions, rather than by managing state as in an imperative programming paradigm. If you ask a computer scientist what differentiates a functional programming language from an imperative programming language, you stand a pretty good chance of being told that the functional language avoids “side effects” — modifications of stored state. Purely functional code returns values, but does not produce side effects; it has output, but does not manipulate state.
Probably the most obvious and visible effect of a functional programming style, upon first introduction to functional code, is the lack of variables. In functional programming, one function simply feeds its output into another function as the latter’s input, in a chain from the first input to the last output of the program. There are exceptions, but they are few and far between, and there are those who would argue that those exceptions are “impurities” in a functional style.
As one might imagine, this approach to programming requires a significant shift in thinking from an imperative approach. Many programmers — perhaps most of us — think of programming in terms of variables. Our imperative programs revolve around those variables, storing input in them, manipulating them via iterative constructs and loops, operating on them, and extracting values from them to produce our output. The state, stored in variables, becomes the center of our programs, with imperative procedures (which we often call “functions” without meaning it in the pure, “functional programming” sense of the term) waiting to be used to operate on that state.
Some languages lend themselves more to functional programming than others. Broadly speaking, most languages can be assigned to a particular programming paradigm: Lisp is functional, Java is object oriented, C is procedural, and Prolog is declarative. None of these is a strictly pure example of the stated paradigm. For instance, Haskell is generally regarded as a more pure functional language than any Lisp-based language — and even Haskell is arguably “impure”.
Most, if not all, strongly object oriented languages are also necessarily strongly imperative languages. Some might object to this characterization; for instance, Common Lisp offers one of the more powerful (and interesting) object models available, and is generally only weakly characterized as imperative, if at all. Many languages capable of an object oriented programming style are also more fully multi-paradigm languages than they are a language of any one or even two specific paradigms — including Perl and Ruby, for instance.
The languages best suited to functional programming are of course those designed first and foremost as functional programming languages, with other paradigmatic characteristics added in to round out the language’s feature set. Such languages, to varying degrees of functional purity, include Common Lisp, Erlang, Haskell, Objective Caml, and Scheme. Next are a collection of languages that are either built with a very paradigm nonspecific philosophy such as Lua and Perl, or have a particular target paradigm but also accomodate additional specific paradigms including functional programming, such as ECMAScript, Python, and Ruby. Finally, there are the languages that accomodate functional programming largely by accident, or only as an afterthought, like C++, Java, and PHP. Some may argue with which languages belong in which group, of course.
Most programmers coming from a primarily imperative programming background will find the languages that fall under the heading of paradigm nonspecific or intentionally object oriented with specifically incorporated functional capabilities quite accomodating for working a little FP magic into their lives. As mentioned above, this class of languages includes ECMAScript, Lua, Perl, Python, and Ruby. In my personal experience, I have found Perl and Ruby to provide very different, but both very accomodating, approaches to a partial functional programming style.
Among many of the most dangerous bugs in a typical imperative program are those that arise because, while we write code, we must be careful to reconcile and account for all the state managed by our programs — and sometimes we fail. Such failures may consist of overlooking some edge case, such as an obscure form a variable’s value may take when an imperative procedure operates on unexpected input. They may consist of simply misjudging the relationship between different collections of state. They may also consist of nothing more than a typo here and there, especially when typing the name of a variable or object. Some such bugs may cause the program to fail to run or compile, but sometimes they may go unnoticed until a malicious security cracker discovers the bug and finds a way to exploit it.
While functional programming does not guarantee the security of the software you build, it can eliminate a whole class of potential problems securing the software that may arise while writing code: state management errors.
Of course, writing truly pure functional code is nearly impossible, if you intend to actually accomplish anything worthwhile. Depending on the language you use and how much you must contort your efforts to suit the goal of pure functional programming, you may also end up with code that is more difficult to understand, which in turn may create other security problems, especially during later code maintenance. The functional programming paradigm is no silver bullet — no single solution to all your software security problems.
On the other hand, all else being equal and within reasonable limits, the more you employ a functional approach to programming, the less opportunity there is to introduce security issues into your code by way of state management errors. As such, it is probably in your best interest to learn how to employ a functional style, if you want to write secure code.