Chapter 1
IN THIS CHAPTER
Exploring functional programming
Programming in the functional way
Finding a language that suits your needs
Locating functional programming resources
This book isn’t about a specific programming language; it’s about a programming paradigm. A paradigm is a framework that expresses a particular set of assumptions, relies on particular ways of thinking through problems, and uses particular methodologies to solve those problems. Consequently, this programming book is different because it doesn’t tell you which language to use; instead, it focuses on the problems you need to solve. The first part of this chapter discusses how the functional programming paradigm accomplishes this task, and the second part points out how functional programming differs from other paradigms you may have used.
The math orientation of functional programming means that you might not create an application using it; you might instead solve straightforward math problems or devise what if scenarios to test. Because functional programming is unique in its approach to solving problems, you might wonder how it actually accomplishes its goals. The third section of this chapter provides a brief overview of how you use the functional programming paradigm to perform various kinds of tasks (including traditional development), and the fourth section tells how some languages follow a pure path to this goal and others follow an impure path. That’s not to say that those following the pure path are any more perfect than those following the impure path; they’re simply different.
Finally, this chapter also discusses a few online resources that you see mentioned in other areas of the book. The functional programming paradigm is popular for solving certain kinds of problems. These resources help you discover the specifics of how people are using functional programming and why they feel that it’s such an important method of working through problems. More important, you’ll discover that many of the people who rely on the functional programming paradigm aren’t actually developers. So, if you aren’t a developer, you may find that you’re already in good company by choosing this paradigm to meet your needs.
Functional programming has somewhat different goals and approaches than other paradigms use. Goals define what the functional programming paradigm is trying to do in forging the approaches used by languages that support it. However, the goals don’t specify a particular implementation; doing that is within the purview of the individual languages.
In contrast to other paradigms, the functional programming paradigm doesn’t maintain state. The use of state enables you to track values between function calls. Other paradigms use state to produce variant results based on environment, such as determining the number of existing objects and doing something different when the number of objects is zero. As a result, calling a functional program function always produces the same result given a particular set of inputs, thereby making functional programs more predictable than those that support state.
Because functional programs don’t maintain state, the data they work with is also immutable, which means that you can’t change it. To change a variable’s value, you must create a new variable. Again, this makes functional programs more predictable than other approaches and could make functional programs easier to run on multiple processors. The following sections provide additional information on how the functional programming paradigm differs.
Imperative programming, the kind of programming that most developers have done until now, is akin to an assembly line, where data moves through a series of steps in a specific order to produce a particular result. The process is fixed and rigid, and the person implementing the process must build a new assembly line every time an application requires a new result. Object-oriented programming (OOP) simply modularizes and hides the steps, but the underlying paradigm is the same. Even with modularization, OOP often doesn’t allow rearrangement of the object code in unanticipated ways because of the underlying interdependencies of the code.
Using pure functions creates a flexible environment in which code order depends on the underlying math. That math models a real-world environment, and as our understanding of that environment changes and evolves, the math model and functional code can change with it — without the usual problems of brittleness that cause imperative code to fail. Modifying functional code is faster and less error prone because the person implementing the change must understand only the math and doesn’t need to know how the underlying code works. In addition, learning how to create functional code can be faster as long as the person understands the math model and its relationship to the real world.
Functional programming also embraces a number of unique coding approaches, such as the capability to pass a function to another function as input. This capability enables you to change application behavior in a predictable manner that isn’t possible using other programming paradigms. As the book progresses, you encounter other such benefits of using functional programming.
Programming languages that use the pure approach to the functional programming paradigm rely on lambda calculus principles, for the most part. In addition, a pure-approach language allows the use of functional programming techniques only, so that the result is always a functional program. The pure-approach language used in this book is Haskell because it provides the purest implementation, according to articles such as the one found on Quora at https://www.quora.com/What-are-the-most-popular-and-powerful-functional-programming-languages
. Haskell is also a relatively popular language, according to the TIOBE index (https://www.tiobe.com/tiobe-index/
). Other pure-approach languages include Lisp, Racket, Erlang, and OCaml.
Many developers have come to see the benefits of functional programming. However, they also don’t want to give up the benefits of their existing language, so they use a language that mixes functional features with one of the other programming paradigms (as described in the “Considering Other Programming Paradigms” section that follows). For example, you can find functional programming features in languages such as C++, C#, and Java. When working with an impure language, you need to exercise care because your code won’t work in a purely functional manner, and the features that you might think will work in one way actually work in another. For example, you can't pass a function to another function in some languages.
You might think that only a few programming paradigms exist besides the functional programming paradigm explored in this book, but the world of development is literally packed with them. That's because no two people truly think completely alike. Each paradigm represents a different approach to the puzzle of conveying a solution to problems by using a particular methodology while making assumptions about things like developer expertise and execution environment. In fact, you can find entire sites that discuss the issue, such as the one at http://cs.lmu.edu/~ray/notes/paradigms/
. Oddly enough, some languages (such as Python) mix and match compatible paradigms to create an entirely new way to perform tasks based on what has happened in the past.
Imperative programming takes a step-by-step approach to performing a task. The developer provides commands that describe precisely how to perform the task from beginning to end. During the process of executing the commands, the code also modifies application state, which includes the application data. The code runs from beginning to end. An imperative application closely mimics the computer hardware, which executes machine code. Machine code is the lowest set of instructions that you can create and is mimicked in early languages, such as assembler.
Procedural programming implements imperative programming, but adds functionality such as code blocks and procedures for breaking up the code. The compiler or interpreter still ends up producing machine code that runs step by step, but the use of procedures makes it easier for a developer to follow the code and understand how it works. Many procedural languages provide a disassembly mode in which you can see the correspondence between the higher-level language and the underlying assembler. Examples of languages that implement the procedural paradigm are C and Pascal.
The procedural paradigm does make reading code easier. However, the relationship between the code and the underlying hardware still makes it hard to relate what the code is doing to the real world. The object-oriented paradigm uses the concept of objects to hide the code, but more important, to make modeling the real world easier. A developer creates code objects that mimic the real-world objects they emulate. These objects include properties, methods, and events to allow the object to behave in a particular manner. Examples of languages that implement the object-oriented paradigm are C++ and Java.
Functional programming actually implements the declarative programming paradigm, but the two paradigms are separate. Other paradigms, such as logic programming, implemented by the Prolog language, also support the declarative programming paradigm. The short view of declarative programming is that it does the following:
It’s essential to remember that functional programming is a paradigm, which means that it doesn’t have an implementation. The basis of functional programming is lambda calculus (https://brilliant.org/wiki/lambda-calculus/
), which is actually a math abstraction. Consequently, when you want to perform tasks by using the functional programming paradigm, you're really looking for a programming language that implements functional programming in a manner that meets your needs. (The next section, “Discovering Languages that Support Functional Programming,” describes the available languages in more detail.) In fact, you may even be performing functional programming tasks in your current language without realizing it. Every time you create and use a lambda function, you’re likely using functional programming techniques (in an impure way, at least).
In addition to using lambda functions, languages that implement the functional programming paradigm have some other features in common. Here is a quick overview of these features:
Various functional language implementations also use different type systems, so the manner in which the underlying computer detects the type of a value changes from language to language. In addition, each language supports its own set of data structures. These kinds of issues aren’t well defined as part of the functional programming paradigm, yet they’re important to creating an application, so you must rely on the language you use to define them for you. Assuming a particular implementation in any given language is a bad idea because it isn’t well defined as part of the paradigm.
To actually use the functional programming paradigm, you need a language that implements it. As with every other paradigm discussed in this chapter, languages often fall short of implementing every idea that the paradigm provides, or they implement these ideas in unusual ways. Consequently, knowing the paradigm’s rules and seeing how the language you select implements them helps you to understand the pros and cons of a particular language better. Also, understanding the paradigm makes comparing one language to another easier. The functional programming paradigm supports two kinds of language implementation, pure and impure, as described in the following sections.
A pure functional programming language is one that implements only the functional programming paradigm. This might seem a bit limited, but when you read through the requirements in the “Using Functional Programming to Perform Tasks” section, earlier in the chapter, you discover that functional programming is mutually exclusive to programming paradigms that have anything to do with the imperative paradigm (which applies to most languages available today).
Trying to discover which language best implements the functional programming paradigm is nearly impossible because everyone has an opinion on the topic. You can find a list of 21 functional programming language implementations with their pros and cons at https://www.slant.co/topics/485/~best-languages-for-learning-functional-programming
.
Python is likely the epitome of the impure language because it supports so many coding styles. That said, the flexibility that Python provides is one reason that people like using it so much: You can code in whatever style you need at the moment. The definition of an impure language is one that doesn’t follow the rules for the functional programming paradigm fully (or at least not fully enough to call it pure). For example, allowing any modification of application state would instantly disqualify a language from consideration.
Functional programming has become extremely popular because it solves so many problems. As covered in this chapter, it also comes with a few limitations, such as an inability to use mutable data; however, for most people, the pros outweigh the cons in situations that allow you to define a problem using pure math. (The lack of mutable data support also has pros, as you discover later, such as an ability to perform multiprocessing with greater ease.) With all this said, it’s great to have resources when discovering a programming paradigm. This book is your first resource, but a single book can’t discuss everything.
Sometimes you can find useful videos online. Of course, you can find a plethora of videos of varying quality on YouTube (https://www.youtube.com/results?search_query=Functional+Programming
), but don’t discount sites, such as tinymce (https://go.tinymce.com/blog/talks-love-functional-programming/
). Because functional programming is a paradigm and most of these videos focus on a specific language, you need to choose the videos you watch with care or you’ll get a skewed view of what the paradigm can provide (as contrasted with the language).