In the dynamic world of coding, does the timeless quote “Less is More” apply? The purpose of this post is to introduce a software development technique that embodies this minimalist approach. It bids goodbye to traditional loops, shuns mutating state, and embraces functions as first-class citizens. Remarkably, all while managing to keep apparent complexities at bay!
This principle finds its roots stemming from the mathematical definition of a function. Famed computer scientist Peter J. Landin once likened this to “maxims which are worth learning, but which have a way of dying unless they’re passed on”.1 As we dissect this paradigm further, you will begin to perceive the truth beyond his words.
Take a glimpse at this code snippet from a popular open-source project ZeroDB, hosted on Github2:
def _create_zodb_root(self, username, password):
from ZODB.DB import DB
from ZEO.ClientStorage import ClientStorage
from .meta_db import ZdbMeta
from .backends.versioned_mongodb import VersionedMongoDBBackend
storage = ClientStorage(
(self._zeo_host, self._zeo_port),
username=username,
password=password,
ssl=self._ssl,
keyfile=self._keyfile,
certfile=self._certfile,
ssl_factory=self._ssl_factory
)
db = DB(storage)
connection = db.open()
root = connection.root()
backend = VersionedMongoDBBackend(self._mongodb_host, self._mongodb_port, self._mongo_ssl)
metadb = ZdbMeta(backend)
root['metadb'] = metadb
import transaction
transaction.commit()
return db
In several lines of code, you can observe that each function performs a unique task, and the state of the program remains untouched. Functions serve as building blocks, moulding a program’s shape and behaviour while maintaining clear boundaries.
This approached, often referred to as Functional Programming (FP), does not merely stop at ‘functions’. It also introduces sweeping concepts like ‘purity’, ‘immutability’, and ‘recursion’.3
A pure function, for instance, can only depend on its input and should always produce the same output. Essentially, purity eradicates side effects which can often introduce unpredictable behaviour in code. Consider the following JavaScript code snippet:
// Impure function
let number = 1;
const increment = () => { number += 1; };
increment();
console.log(number); // prints 2
// Pure function
const increment_pure = (num) => { return num + 1; }
console.log(increment_pure(1)); // prints 2
In the first function, an external state (‘number’) is mutated whereas in the ‘pure’ function, the original input remains untouched; following micro and macrocosmic order.
Next up, we have the idea of ‘immutability’. The notion here is simple: once a variable is created, it cannot be changed4. As unconventional as it might sound, immutable data structures can greatly improve the readability and reliability of your code. Functional languages such as Haskell, Clojure, and Elm rigorously enforce immutability. Here’s an example in Lisp:
;; List definition
(setf animals '(cat dog bird))
;; Appending an animal
(setf animals (append animals '(fish)))
;; animals is now '(cat dog bird fish)
‘animals’ remains untouched when a new animal is added. Instead, a new list gets created, highlighting the charm of immutable data structures and their contributions to maintaining code purity.
While these concepts might seem intimidating initially, adopting them would yield more predictable and maintainable code. In fact, even mainstream non-functional languages, like Java and C#, have begun incorporating functional methods in their more recent iterations5.
It might be tempting to jump onto the functional bandwagon at this point. Before you do, be aware of “gotchas and pitfalls.” While functional programming methodologies promote brevity and simplicity, it does have an inherent cognitive overhead. Concepts such as lazy evaluation, recursion or monad can pose steep learning curves for beginners. Getting familiar with functional idioms requires time, patience, and, often, a paradigm shift in one’s thinking. But the pay-off can be rewarding in the long run!
In conclusion, the functional paradigm serves as a powerful and elegant vehicle to construct complex solutions with simplicity. Its focus on purity and immutability offers developers an architectural framework that is robust and markedly distinct from the traditional procedural programming model.
As a closing note, let’s reminisce over a quote by the revered computer scientist Edsger W. Dijkstra, “Simplicity is prerequisite for reliability”6. That, indeed, could serve as the cornerstone philosophy that functional programming embraces!
Landin, P. J. (1966). The Next 700 Programming Languages. Communications of the ACM, 9(3), 157–166. doi:10.1145/365230.365257 ↩︎
ZeroDB. (n.d.). GitHub. www.github.com/zerodb/ ↩︎
Chiusano, P., & Bjarnason, R. (2014). Functional Programming in Scala. Manning. ↩︎
Immutability in JavaScript, Dev.to, www.dev.to/skaytech/immutability-in-javascript-j8f ↩︎
Flanagan, D. (2011). JavaScript: The Definitive Guide. O’Reilly & Associates. ↩︎
Dijkstra, E. W. (1989). On the Cruelty of Really Teaching Computing Science. EWD 1036. University of Sussex. ↩︎