• Functional Programming for Android Developers — Part 1
  • Anup Cowkur
  • The Nuggets translation Project
  • Translator: skyar2009
  • Proofread by: Danny1451, yunshuipiao

I recently spent some time learning Elixir, an excellent programming language for beginners.

I wonder, why don’t we use the ideas and techniques of functional programming in Android development?

When most people hear about functional programming, they think of Hacker News posts about monads, higher-order functions, and abstract data types. It seems to be an arcane realm far removed from the usual hard coding programmers, and only the domain of powerful hackers.

Leave it alone! I would say you can learn it, you can use it, and you can build beautiful applications with it — elegant, readable, bug-free code.

Welcome to How Android Developers program functionally (FP). In the next series of articles, I’ll walk you through the basics of FP and how to use FP in older versions of Java. This article is intended to be practical, and academic remarks will be kept to a minimum.

FP is a big topic. We’ll cover only ideas and techniques that are useful for writing Android code. You may see some ideas that are not directly applicable for completeness reasons, but I will try to keep the material as relevant as possible.

Are you ready? Let’s get started.

What is functional programming? Why should I use it?

That’s a good question. Functional programming is an umbrella for a set of programming ideas that have been treated unfairly. At its core, it is a way of programming that treats a program as an evaluation of a mathematical method, with no state changes and no side effects (which we’ll talk about in a moment).

The core idea of FP emphasizes:

  • Declarative code – the programmer should care what it is, and let the compiler and runtime environment care how it is done.
  • Clarity — The code should be as obvious as possible. In particular, isolate side effects to avoid accidents. To clearly define data flow and error handling, avoid GOTO statements and exceptions because they can put your application in an unexpected state.
  • Concurrency – Because of the pure function concept, most functional code is parallel by default. Since CPUS are not running as fast as they used to year after year (see Moore’s Law), it is generally believed that this trait has led to the growing popularity of functional programming. And we must also take advantage of the multi-core architecture to make our code as parallel as possible.
  • Higher-order functions – Functions, like other language primitives, are first-class citizens. You can pass functions like strings and ints.
  • Immutability – variables cannot be modified once initialized. Once created, it will never change. If changes are needed, new ones need to be created. This is another aspect of clarity and avoiding side effects. If you know a variable can’t be changed, you’ll be more confident about its state when you use it.

Isn’t declarative, specific, and concurrent code easier to derive and designed to avoid surprises? I hope I’ve piqued your interest.

As part one of this series of articles, we start with some basic concepts of FP: purity, side effects, and sequencing.

Side effects

To explore this concept, let’s modify the original function to store the results in a file.

int add(int x, int y) {
    int result = x + y;
    writeResultToFile(result);
    return result;
}
Copy the code

This function writes the result to a file, which modifies the state of the outside world. So the function has side effects, it’s not pure anymore.

Any code that modifies external state (modifying variables, writing files, storing DB, deleting content, etc.) has side effects.

Functions with side effects should be avoided in FP because they are no longer pure functions but depend on historical context. The context of the code is not determined by itself, which makes it harder to derive.

Let’s say you write a piece of code that depends on the cache. The output of the code depends on whether someone has written to the cache, what was written to it, when it was written, whether the data written is valid, and so on. You can’t know what your program is doing unless you know all the possible states of the cache it depends on. If you expand your code to include everything your application depends on — networks, databases, files, user input, and so on — it becomes hard to know exactly what’s going on and to consider everything at once.

Does this mean we don’t use networks, databases, and caches anymore? Of course not. When the execution is done, the application often needs to do something. For Android apps, for example, it’s often about updating the UI so that users actually get useful content from our apps.

FP’s greatest concept is not to abandon side effects altogether, but to embrace and isolate them. We keep side effects at the edge of the system to minimize the impact, make the application easier to understand, and avoid messing up the application with side effects functions. We will discuss this in detail in a later article in this series when we examine functional architectures for applications.

conclusion

Hopefully the first task has piqued your interest in FP. Pure, side-effect-free functions make code more readable and are the first step to parallelism.

Before we start implementing parallelism, we need to know something about immutability. We’ll explore this in part 2 of this series and see how pure functions and immutability can help us write easy-to-understand parallel code without resorting to locks and mutexes.