Avatar LogoJeff Thomas

Functional Programming Series: Introduction

written byJeff Thomas

Functional Programming|Series|TypeScript

Published: March 18, 2023

17 min read |
Functional Programming Series: Introduction

Photo by: Steve Johnson on Unsplash

Introduction

The distinction between past, present, and future is only a stubbornly persistent illusion.

Albert Einstein

As a software engineer with over 20 years of experience, mainly focused on object-oriented programming (OOP), I must admit that many of the concepts in functional programming (FP) seem foreign to me. However, I believe that as professionals, we should never stop learning and challenging ourselves. So, in this series of blog posts, I will be learning in public, trying to write to better understand FP concepts, and sharing my knowledge and experiences with you.

Everything old is new again, and the same holds true for functional programming. Despite being an old concept, functional programming has become increasingly popular in recent years. In this series, we will explore the world of functional programming, with a focus on TypeScript. We will cover the basics to advanced concepts of FP, starting with an overview of the history of the paradigm shift from OOP to FP and why it is becoming more popular. We will examine how FP is well-suited for building scalable and reliable applications, and we will look at how it can be applied to real-world scenarios.

I chose to focus on TypeScript as it is widely used in web development and has a large community of developers. Additionally, the language has adopted many functional programming features, making it ideal for demonstrating FP concepts.

In this first post of the series, I will provide an overview of functional programming looking at the paradigm shift from OOP to FP, and why it is becoming more popular. By the end of this post, you should have an understanding of functional programming's history and how it differs from OOP as well as why TypeScript is an excellent language for demonstrating FP concepts.

So, join me on this journey into functional programming in TypeScript, and let's explore this fascinating world of programming together!

Paradigm Shift

In terms of programming paradigms, I had exposure to various paradigms during my coursework and used multiple programming languages throughout my career. However, I primarily used the Object-Oriented (OO) Imperative paradigm. When I decided to learn Functional Programming (FP), I initially thought it would be similar to learning another programming language. However, I quickly realized that it was a different paradigm altogether.

Functional Programming is called a different paradigm because it involves a distinct set of rules, strategies, styles, and ways of programming. Unlike learning a new language in the same paradigm, switching to a different paradigm is not as straightforward.

Programming Language Paradigms
  1. Imperative: Programming that involves a specific sequence of commands that update state (e.g., do this, then that).
    • Procedural: Imperative programming that utilizes procedure calls (e.g., C, Pascal).
    • Object-Oriented: Programming that revolves around defining objects that communicate with each other through messages. Objects contain encapsulated internal states and public interfaces (e.g., C++, Java (class-based)).
  2. Declarative: Programming that involves specifying the desired result, rather than how to achieve it (e.g., this is what I want, do it in any way you see fit).
    • Functional (Applicative): Programming that revolves around function calls, without utilizing any global state (e.g., Haskell, LISP).
    • Logic (Rule-based): Programming that involves specifying a set of rules and facts. An engine infers answers to questions based on these rules and facts (e.g., Prolog).

JavaScript is a multi-paradigm language (e.g., procedural, OO Prototype-based, and functional).

In declarative programming, you decide what you want instead of the imperative way of describing how you want it.

Object oriented programming makes code understandable by encapsulating moving parts. Functional programming makes code understandable by minimizing moving parts.

Michael Feathers

What is Functional Programming?

FP has it's roots in mathematics evolving from the lambda calculus. Lambda calculus was introduced by Alonzo Church in 1932. His work predated the work Alan Turing was doing with Turing machines. Working on similar problems, they both wrote the Church-Turing Thesis in 1936 proving that the lambda calculus and the Turing machine were compatible. A Turing machine took a bottom up approach (imperative) and the lambda calculus took a top down approach (declarative).

In lambda calculus, functions are king. Everything is a function, including numbers. Thinking of functions as the atomic building blocks (the legos from which we construct our creations) is a remarkably expressive and eloquent way to compose software.

Composable Software by Eric Elliot

Aside from the lambda calculus, category theory (CT) is also utilized in functional programming. Category theory is one of the most abstract parts of mathematics. Compared with the lambda calculus, it is much harder to grasp. Category theory is related to the lambda calculus mostly because it provides a nice way to understand types. Types in FP have an underlying algebraic structure which can be best understood by categorical means. Programmers new to category theory can learn more about it from Bartosz Milewski's online book Category Theory for Programmers. We will discuss category theory and types in later posts in this series. For now the main focus will be on the lambda calculus.

From a programmer's perspective, functional programming is about side-effect free code and thinking in terms of the data flow throughout an application.

When people talk about functional programming, they mention a dizzying number of "functional" characteristics. They mention immutable data, first class functions and tail call optimisation. These are language features that aid functional programming. They mention mapping, reducing, pipelining, recursing, currying and the use of higher order functions. These are programming techniques used to write functional code. They mention parallelization, lazy evaluation and determinism. These are advantageous properties of functional programs.

Ignore all that. Functional code is characterised by one thing: the absence of side effects. It doesn't rely on data outside the current function, and it doesn't change data that exists outside the current function. Every other "functional" thing can be derived from this property. Use it as a guide rope as you learn.

An introduction to functional programming by Mary Rose Cook

Unlike imperative object-oriented paradigms, FP does not use classes, control flow structures like if/else or try/catch, error throwing, iterative loops with for and while, and shared state (mutation). This is about the point most programmers ask how you can write a program without these if they come from an OO paradigm. Throughout this series, we will learn to program using functional alternatives to these concepts.

Unfortunately, no one can be told what The Matrix is. You'll have to see it for yourself.

Morpheus in The Matrix

Keep in mind concepts and patterns that are normal in one paradigm (e.g., OOP - classes, iteration, control flow, error throwing) may not be great in another (e.g., FP - functions, recursion, data flow).

Functional programming favors:

  • Pure functions over shared state and side effects
  • Immutability over mutable data
  • Function composition over imperative flow control
  • Generic utilities that act on many data types over object methods that only operate on their colocated data
  • Declarative over imperative code (what to do, rather than how to do it)
  • Expressions over statements

Composable Software by Eric Elliot

We will explore the meaning of these concepts throughout this series, and it's important to approach them with an open mind. Learning a new programming paradigm after working with another can often lead to feeling like the new paradigm is "wrong" or not adhering to The Right Way™ of doing things. However, as I delved into FP, I began to realize that my previous experience with OO was not necessarily the only way to approach programming. Ultimately, choosing the right paradigm for a given problem depends on many factors. Building an intuition for when to use each mindset can take time, but each paradigm offers a unique and valuable perspective on programming. For example, FP is especially useful for data transformations or high-stakes applications where it's crucial to have complete confidence in the accuracy of the program's results.

Is it not true that the word for 'not functional' is 'dysfunctional'?

The Dao of Immutability by Eric Elliot

History

Some of this misguided love/hate relationship between paradigms like OO and FP stems from not fully appreciating our history. Alan Kay, who coined the term "Object-Oriented Programming," defined it quite differently from what we think of as OO today. His functional object-oriented Smalltalk language emphasized message passing, component encapsulation, and dynamic/late binding at runtime rather than compile time. Interestingly, Kay did not consider features like classes, inheritance, polymorphism, static types, class types, and the special treatment of objects like the new keyword essential or necessary.

I made up the term 'object-oriented', and I can tell you I didn't have C++ in mind.

Alan Kay, OOPSLA '97

Unfortunately, in the 70s and 80s, this vision was distorted with the rise of languages like C++ and further in the 90s with the popularity of Java, which included features like inheritance hierarchies. As a result, OO languages with these distortions have come to dominate the programming world, much to Alan Kay's lament.

I'm sorry that I long ago coined the term 'objects' for this topic because it gets many people to focus on the lesser idea. The big idea is messaging.

Alan Kay On Messaging

Other thought leaders have also commented on the issue. In 1977, John Backus introduced an algebraic approach to programming that deviated from the conventional von Neumann style of programming that is still prevalent today. Backus's paper was ahead of its time and has had a significant impact on functional programming. He critiqued traditional programming languages, and his vision for Functional Programming emerged as a solution.

Conventional programming languages are growing ever more enormous, but not stronger. Inherent defects at the most basic level cause them to be both fat and weak: their primitive word-at-a-time style of programming inherited from their common ancestor — the von Neumann computer, their close coupling of semantics to state transitions, their division of programming into a world of expressions and a world of statements, their inability to effectively use powerful combining forms for building new programs from existing ones, and their lack of useful mathematical properties for reasoning about programs.

Can Programming Be Liberated from the von Neumann Style? A Functional Style and Its Algebra of Programs

Since the resurgence of functional programming around 2015, we have been re-examining programming history through a new lens. We now understand that what Alan Kay defined as OOP is actually compatible with FP. We are also seeing JavaScript, once regarded as a "toy scripting language," now being widely adopted and evolving with new language features and a fresh approach to programming with a blend of old and new styles. Similarly, some languages such as Java, which previously distorted OOP, are now incorporating functional programming concepts and features (e.g., Lambda Expressions) into the language, in addition to promoting conventions like composition over inheritance to guide developers.

Watch Richard Feldman deliver an entertaining talk asking Why Isn't Functional Programming the Norm?.

Why Functional Programming?

Functional programming is not without its downsides. The barrier to entry for learning this paradigm is steep, making it difficult for teams to agree to use this style in their products. The functional paradigm imposes restrictions and rules that may be easier to implement in greenfield projects but can also be gradually incorporated into existing brownfield projects. However, these rules are not meant to limit what can be achieved. The restrictions are meant to empower developers by producing extremely valuable benefits beyond just being trendy or showing off technical knowledge.

Functional programming benefits include the following:

  • Predictable, safe, and less error-prone code
  • Easier to test and debug: Functional programming relies on concise, focused, and contained functions where mocks are not typically needed, as the state of the world does not matter.
  • Easier to read, reason about, and maintain: There is no need to play detective following breadcrumbs throughout a codebase, as functions are self-contained and do not rely on or impact the outside world.
  • Support for parallelism: Although JavaScript is single-threaded, browsers can take advantage of multiple processors. Functional programming supports concurrency well, while object-oriented programming (OOP) creates difficulties (e.g., mutation, shared pointers) with race conditions and locks.
  • No shared state: Shared state is often the cause of complex bugs, and functional programming eliminates this problem by avoiding shared state altogether.

These concepts are ubiquitous in the tech industry, appearing in technologies such as Docker (immutable images), Kubernetes (including sidecars), event streams (such as Kafka), and Git.

If you want to see which features will be in mainstream programming languages tomorrow, then take a look at functional programming languages today.

Simon Peyton Jones

Why Functional JavaScript?

Pure functional languages like Haskell have many of the FP concepts baked in making it much easier to write in the FP style. JavaScript is missing key functional concepts like:

ConceptPure FP LanguageJavaScript
PurityNo side effects are permitted and are actually enforced by the language.Achieved through convention.
ImmutabilityNo state mutation is enforced by the language.No immutable data structures. JavaScript implemented const and Object.freeze(), but these fall short. const doesn't allow reassignment, but we can change the object it refers to. Object.freeze() is shallow by locking the root object, but not any nested objects. However, there are many good libraries like Immutable.js, Immer, and Mori that fix these issues.
RecursionProper tail calls are supported out of the box.Support for tail call optimization was recently added to JavaScript, however only one browser actually implemented them.

So why would we write JavaScript using the FP style? JavaScript is not a pure functional language, but is suitable to functional programming because it has:

  • First class functions,
  • Anonymous functions with a concise syntax, and
  • Closures.

JavaScript is not a functional programming language like Lisp or Haskell, but the fact that JavaScript can manipulate functions as objects means that we can use functional programming techniques in JavaScript.

David Flanagan, JavaScript: The Definitive Guide, 6th Edition

JavaScript's multi-paradigm nature allows developers to choose the best approach for a given problem, making it a versatile language. For brownfield codebases, transitioning to functional programming can be done gradually, similar to adopting TDD practices. On the other hand, object-oriented JavaScript can be challenging to work with due to complexities like prototypes, this, and outside context. The JavaScript FP community and tools are well-established and provide reliable support.

Apps ate the world, the web ate apps, and JavaScript ate the web.

Eric Elliott

Modern examples of real-world JavaScript using FP include Netflix (e.g., Angular 2), Facebook and Instagram (e.g., React), and PayPal (e.g., Redux). Some of the most used JavaScript libraries and frameworks are built using FP principles like RxJS, Lodash, and Ramda.

The way forward is sometimes the way back.

Labyrinth

The JavaScript language has added a number of functional programming features (e.g., map, filter, reduce) in recent years and more are planned (e.g., |>). The future features of JavaScript have their roots in functional programming. Bottom line is that JavaScript is everywhere, it's the most widely used programming language, and it's what real companies use.

Always bet on JS!

Brendan Eich

Why TypeScript?

TypeScript is a programming language, a compiler, and a language server that can be thought of as a linter and static code analysis tool. TypeScript is a syntactic superset of JavaScript that has optional typing and compiles to plain JavaScript.

There are many reasons for combining FP and TypeScript.

  • JavaScript lacks type signatures as its dynamically typed. TypeScript adds static typing to JavaScript. Types are one of the best forms of documentation you can have. Mathematically thinking, the function signature is a theorem and the function body is the proof. We will learn more about type systems and why static types are important in functional programming in later posts in this series.
  • Types increase your agility when refactoring. It's better for the compiler to catch errors than to have things fail at runtime.
  • TypeScript and FP are great at minimizing bugs in your code. A 2017 study found that 15% of all JavaScript bugs can be detected by TypeScript with overall code quality greatly improved!
  • Since TypeScript is a superset of JavaScript, it allows for gradual adoption.
  • A goal of TypeScript is to provide planned features from future JavaScript specifications to current JavaScript engines. You can start using tomorrow's features today.
  • The core of VS Code, the most popular IDE for web development, is built using TypeScript.
  • Goodbye 'undefined' is not a function. Need I say more?

But there are many statically-typed languages that compile to JavaScript. Some are similarly supersets of JavaScript too. Additionally, some are more suited for functional programming like PureScript, Elm, Reason, Flow, and more by the day it would seem. So why TypeScript?

  • The 2020 State of JavaScript survey found that TypeScript was the "Most Adopted Technology." It stated that "TypeScript was already popular, and with a +14.7% growth in usage it looks like it's only going to get more ubiquitous going forward."

I skate to where the puck is going to be, not where it has been.

Wayne Gretzky

In short, TypeScript has won.

Summary

In the first post of my series on functional programming, I covered the basics of imperative and declarative programming paradigms. I discussed the history and benefits of functional programming, including its focus on immutability, composition, and pure functions.

I also explained why I chose to use TypeScript for this series, highlighting its type system, which can help catch errors at compile time, and its support for functional programming concepts such as first class functions and closures.

Throughout the post, I emphasized that functional programming is not without its downsides, such as the steep learning curve, but argued that the benefits make it a valuable tool for building scalable, maintainable applications.

Overall, I aimed to provide a brief introduction to functional programming and set the stage for future posts in the series, where I will dive deeper into specific concepts and demonstrate how they can be applied in TypeScript.

main
git log
Comments

To leave feedback or questions, simply login using your preferred social network. I will read and answer your comments promptly, but please keep in mind that they will be public.

No comments yet.
main