What does "functional programming" mean to you?
Tags: functional, haskell, scala, erlang
Published: 2021-11-18 20:11:55

> _Photo by [Magda Ehlers](https://www.pexels.com/@magda-ehlers-pexels) from Pexels_

What crosses your mind when you hear the phrase "functional programming"? Some people imagine arcane, esoteric code, built around functors and monads and applicatives, but I think of

1. **immutability and referential transparency**

    Immutability just means you create new objects instead of _mutating_ (changing) old ones. This means every variable is effectively `final` or `const` or whatever your language calls it. Instead of

    ```java
    public class Dog {
      public final String name;

      public Dog(String name) {
        this.name = name;
      }

      public void rename(String newName) {
        this.name = newName;
      }
    }
    ```
    <br/>your `rename`d `Dog` would be a new object, à la

    ```java
    public class Dog {
      public final String name;

      public Dog(String name) {
        this.name = name;
      }

      public Dog rename(String newName) {
        return new Dog(newName);
      }
    }
    ```

    <br/>Immutability makes it easy to reason about objects (`Dog doug = new Dog("doug")` will always have `name.equals("doug")`), but it can make your program less efficient, both in terms of memory usage and in terms of performance -- always creating new objects takes more time and space than reusing existing ones.

    Referential transparency is a closely-related concept. In a nutshell, what it means is that -- wherever you have the value `x` in your code, you can replace it with whatever you initially declared `x` to be. So if `const x = 10`, then `x` _always_ equals `10`. You can replace every instance of `x` with the literal `10` and your program should do the exact same thing. (This does not work, of course, if your objects and variables are mutable and you can do something like `x = x + 1`!)

2. **higher-order functions**

    In many languages, functions and objects are separate things: you can pass objects as arguments to functions, and functions can be scoped as object _methods_. But languages which encourage a functional style will allow you to pass functions as arguments to functions, like in Scala

    ```scala
    def process(text: String, fs: (String => Unit)*): Unit = {
      fs.foreach(f => f(text))
    }

    process("Hello, World!", println)
    ```

    <br/>The above `process` method takes a `String` argument `text` and a `(String => Unit)*` argument `fs` -- that is, zero or more functions which themselves take a `String` and return `Unit`, Scala's equivalent of `void`. Once you get used to working with higher-order functions, their utility is immense

    ```scala
    import Console._
    process("this does nothing")
    process("print to stdout", out.println)
    process("print to stderr", err.println)
    process("print to both", out.println, err.println)
    process("etc", out.println, err.println, myLogFunc)
    ```

    <br/>Adding a new output device is as easy as passing a new function to `process`. Treating functions as "first-class citizens" of a language opens a huge array of new possibilities.

3. **pure functions (side-effect-free functions)**

    A "pure" function is a function without "side effects". What does that mean? In a nutshell, it means that a function should behave similar to a _mathematical_ function (think `y = f(x)`) -- it should take some value, `x`, and _return_ or _become_ some other value, `y`.

    Mathematical functions don't write to log files or print text to a terminal or change the value of other variables on the page -- they turn `x` into `y`, that's it.

    Of course, computer programs are kind of useless if they can't take input from a user, or read a config file, or communicate with external resources. Input / Output, or IO, is required to make programs "interactive". Functions which perform output (typically returning no value) typically return a `void` or `Unit` or `null` value of some kind. Haskell, for instance, uses its [IO monad](https://wiki.haskell.org/Introduction_to_IO) for input and output.

    We can't eliminate side-effecting functions, but we should try to make it as clear as possible what the intent of our functions are. If you want to calculate a value, and log it to a file, [separate those concerns](https://en.wikipedia.org/wiki/Separation_of_concerns) into two separate routines, if possible.

4. **declarative programming and lambdas**

    "Functional programming" also makes me think of a particular _style_ of programming: iterating over data structures (like arrays, dictionaries, maps, sets, etc.) using _declarative_-style functions like `map`, `filter`, `foreach`, and so on.

    _Imperative_ programming is (in a sense) the opposite of declarative programming. An imperative program declares exactly what is to be done:

    ```c
    int[10] array = ...
    double sum = 0;
    int ii = 0;
    for (ii = 0; ii < 10; ++ii) {
        sum += array[ii];
    }
    ```

    <br/>A declarative program specifies _what_ is to be done, and the language itself figures out how to do that. For example, in a declarative program, we might rewrite the above as

    ```scala
    var sum: Double = 0
    array.foreach(x => sum += x) // lambda
    ```

    <br/>...or

    ```scala
    val sum = array.fold((partialSum, x) => partialSum += x)
    ```

    <br/>...or even just `array.sum`. Functional programming also tends to make heavy use of "lambda" functions, like the one used above, `x => sum += x`. This is an anonymous function (it doesn't have a name like `addToSum`) which takes a value `x` and adds it to the value `sum`.

    Declarative programming, and lambda functions, are hallmarks of functional programming, in my view.

5. **stream processing**

    All of the above leads me to routinely return to the (not at all novel) metaphor of plumbing for functional programming. You put data in one end of a "processing pipeline", like water entering a pipe. It may be split into multiple streams, sent down different paths, siphoned off into some database, or dumped ultimately into some data lake, but the data moves from producers / sources (input), through flows / transformers / conductors, and into consumers / sinks.

    Stream processing is quite similar to reactive programming, which has a whole [manifesto](https://www.reactivemanifesto.org/) and is implemented in several languages (here's one for [Scala](https://doc.akka.io/docs/akka/current/general/stream/stream-design.html), and another for [R](https://shiny.rstudio.com/articles/reactivity-overview.html)).

    Remember:

    > _"The Internet is... a series of tubes."_
    > -- U.S. Senator Ted Stevens [[source]](https://en.wikipedia.org/wiki/Series_of_tubes#Partial_text_of_Stevens's_comments)

---

[Wikipedia](https://en.wikipedia.org/wiki/Functional_programming) also mentions lazy evaluation and recursion, but these don't immediately jump into my mind as "pillars" of functional programming.

How about you? What comes to _your_ mind when someone says "functional programming"?