Functional programming languages are useless in the real world, they say. And you know, they’re more correct than I usually want to admit. Firms really only use functional languages, if at all, for infrastructure. It doesn’t usually make sense to recruit Haskellers or to teach your engineers Lisp, when Java or PHP or Ruby can get the job done sufficiently.
Regardless, important design patterns are emergent properties of functional programming languages, and they are useful to adopt in all other programming paradigms.
Avoid mutation. Changing a variable’s value makes it more challenging to reason about code correctness. Since functional programming, derived from the Lambda calculus, is about persistent state, one logically should assign new variables for new computations instead of overwriting old ones.
Mutate and perform I/O at the top level. Haskell explicitly marks I/O and mutation through its type system; a String read from a file is different than a normal String and cannot be treated like a normal String. This is important because it forces a program’s logic to be simpler; retrieve all needed outside information, perform computations, and then use the results as necessary.
A method should do one thing. Pure functional languages do not have imperative statements, only expressions; therefore, a function is constrained to do one useful thing. This, again, permits easy reasoning about code; it also permits easy testing of code. Finally, it allows methods to be reused and recycled.
A method should be its own documentation. As a consequence of the above guideline, the types of a method’s inputted parameters and outputted value should sufficiently explain what the method does. For example, it should be obvious that a nameless method of the HashMap<K,V> class that takes a K and returns a V is performing a hash table lookup. If this does not hold true for a method, it is likely too complicated.
Computation should be composed. Since functional methods are broken into one-bite pieces, it is common to see multiple methods chained together, the output of one serving as the input to another. Given some array of objects, you may wish to grab a specific parameter from each object, apply a transformation to said parameter, and produce a condensed, representative value. (For example — grabbing a user’s tweets, plucking the number of retweets each post has, and summing to find total retweets.) This should comprise three method calls.
Embrace recursion. Due to its lack of mutation, functional programming languages eschew iteration in favor of recursion. The two are equally powerful, yet recursion may oftentimes be the simpler implementation. Because functional programming languages force first-day coders to contemplate recursion, functional programmers are better-prepared to implement and employ tree and graph data structures, whose algorithms are significantly clearer with recursion. Since a recursive data structure may be the best tool for a job, learning recursion competently will permit you to write better code in less time.
To reiterate, this is not an argument to switch your tech shop to OCaml. This is an argument to learn and use functional design patterns. Programming paradigms are not discrete absolutes, but rather form a continuous spectrum. Moving toward the functional paradigm will avoid code bloat, simplify testing, aid correctness, and improve productivity.