Dec 04 2017
I’ve been asked at least once “How can I get better at programming in a compositional style, with functions?” This question usually stems from someone trying to learn a language dedicated to functional programming and struggling to make the paradigm feel “natural”. As one of the more outspoken advocates of functional programming in the office, I thought I’d provide a “listicle” of tips in the inimitable style of BuzzFeed to address the question.
Functional programming is all about building up complicated solutions from small, easy to understand parts. Getting better at this means avoiding the monolithic, magical frameworks and libraries that do everything for you in favor of assembling all the parts of a problem yourself. You don’t need to work in a fancy language like Clojure or Haskell to get better at this either (though it doesn’t hurt!). For example, instead of choosing Rails for your next web project, try going with something lighter like Sinatra and filling in any missing pieces yourself. This forces you to really think about the requirements of the problem you’re solving and consciously find, write and glue together the parts that make a good solution.
When you write in a compositional style your program ends up being the sum of many interconnected parts. Values flow through those parts to make interesting work happen. Just as a ten-inch pipe and a twelve-inch pipe have different jobs, and you can’t connect them without an adapter, you can’t expect all those interconnected functions to work without some adapters. A type system, static analyzer, or extensive unit tests can help you quickly find where you need those adapters. Haskell has a solid type-system, Erlang/Elixir has a static analyzer in Dialyzer, and Clojure has a type system in core.typed as well as spec which can automatically generate unit tests for your functions.
Fundamentally, a functional programming language works with functions. These work much more like mathematical functions from algebra as opposed to working like procedures from a language like C or Python. These functions take a specific set of values as input and return a specific set of values as output. Which values it returns as output given specific inputs defines the behavior of the function. As an example, consider a function that returns the length of a string. As its input set it can receive any string, and as its output it can return any positive integer including 0. It can’t return a negative number, nor can it return floating point numbers. Additionally it’s behavior isn’t defined if you give it something different, like a boolean value. Keeping this principle in mind will help you gain an intuition for when your compositions will “just work.” Oh, and a type checker helps here too.
There are all sorts of properties a function can obey. These can be internal to the function itself, defining its behavior in isolation, or they can be external, defining the behavior in relation to other functions. Internal properties are going to determine which inputs map to which outputs, and consequently define the behavior of the function. External properties are going to make sure your function doesn’t have surprising behavior or edge cases in compositions. An example of an internal property is referential transparency, or the function returns the same output when given the same input. An example of an external property is associativity, or can I evaluate the function in any order I like?
Most of these tips aren’t specific to languages designed for functional programming. Being able to build large systems out of smaller, well-understood parts is a valuable skill in any paradigm. Getting good at it will not only improve your functional code, but it will also improve the way you write object-oriented code as well. Use these tips to guide the way you practice and we’ll all write safer, easier to understand code.