Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Functional Programming: A Paradigm

DZone's Guide to

Functional Programming: A Paradigm

There are two main components that make up functional programming — pure functions and immutable values. Click here to learn more about the FP paradigm.

· Java Zone ·
Free Resource

How do you break a Monolith into Microservices at Scale? This ebook shows strategies and techniques for building scalable and resilient microservices.

Scala 3.0

It’s surprisingly hard to find a consistent definition of functional programming. But, I think you can define FP with just two simple statements:

1. FP is about writing software applications using only pure functions.
2. When writing FP code, you only use immutable values.

Therefore, functional programming is a way of writing software applications using only pure functions and immutable values. Now, let further explore the two statements that we stated above.

Pure Functions

A pure function can be defined as:

  • The output of a pure function depends only on (a) its input parameters and (b) its internal algorithm, which is unlike an OOP method and can depend on other fields in the same class as the method.
  • A pure function has no side effects. It does not read anything from the outside world or write anything to the outside world. For example, it does not read from a file, web service, UI, or database, and does not write anything either.
  • As a result of those first two statements, if a pure function is called with an input parameter x an infinite number of times, it will always return the same result y. For instance, any time a “string length” function is called with the string “Ayush,” the result will always be five.
  • Here is an example of a pure function:
    def sum(firstNumber: Int, secondNumber): Int = {
        firstNumber + secondNumber
    }

Immutable Values

The best functional programming code is like algebra, and, in algebra, you never reuse variables. By avoiding the re-use of variables, we can get benefits like:

  • You can always rely on a value to replace it with some functionality. For instance, you have val a = f(x). Here, you can always use the value a as a replacement for f(x), as its value can’t be changed, which is not the case with var a = f(x). Hence, the algebraic substitutions are possible.
    val a = f(x)
    val b = g(a)
    val c = h(b)                                                                                        1                        val a = f(x)                      2                        val b = g(a)                      3                        val c = h(b)                              


  • Here, a and b can never change; the code is easier to reason about. With var fields, you always need to have a background thread running in your mind, “Is a reassigned somewhere else? Keep an eye out for it.”
  • Another reason to use only immutable values is that mutable variables (var fields) don’t work well with parallel/concurrent applications. The reason for this being that concurrency is becoming more important as CPUs use more cores.

Benefits of Functional Programming

  • Pure functions are easier to reason about:
    You know that they can’t do certain things, such as talk to the outside world, have hidden inputs, or modify the hidden state. Because of this, you’re guaranteed that their function signatures tell you two things — (a) exactly what’s going into each function and (b) coming out of each function.
  • Testing is easier, and pure functions lend themselves well to techniques like property-based testing:
    It’s easier to test pure functions because you don’t have to worry about them dealing with hidden state and side effects.
  • Debugging is easier: 
    Because pure functions depend only on their input parameters to produce their output, debugging applications written with pure functions is easier. You don’t have to worry about what’s going on in the rest of the application, you just have to know the inputs that were given to the pure function that failed.
  • Programs are more bulletproof:
    There are fewer “moving parts” — mutable variables and hidden state — in FP applications, mathematically speaking, the overall application is less complex.
  • Pure Function signatures are more meaningful:
    Non-FP methods can have side effects, i.e., hidden inputs and outputs of those methods. Ultimately, their function signatures often don’t mean that much.
    def doSomething(): Unit { code here ... }

    It takes no input parameters and returns nothing. Therefore, there’s no way to guess from the signature what this method does. In contrast, because pure functions depend only on their input parameters to produce their output, their function signatures are extremely meaningful.
  • Parallel/concurrent programming is easier:
    A functional program is ready for concurrency without any further modifications. You never have to worry about deadlocks and race conditions because you don’t need to use locks. No piece of data in a functional program is modified twice by the same thread — let alone by two different threads. That means that you can easily add threads without ever giving conventional problems that plague concurrency applications a second thought.

Disadvantages of Functional Programming

There are a few disadvantages of FP as well, but don’t worry; there is a way around every problem.

  • Writing pure functions is easy, but combining them into a complete application is where things get hard:
    All of the functions follow the same pattern — (1) data in, (2) apply an algorithm (to transform the data), and (3) data outGluing pure functions to create a complete FP application is one of the biggest stumbling blocks you’ll encounter. But, there are solutions to this, but that is a topic for another day.
  • Advanced maths terminologies make FP intimidating:
    When you first hear terms like combinator, monoid, monad, and functor, you find these terminologies intimidating and that fear factor becomes a barrier to learning FP.
  • For many people, recursion doesn’t feel natural:
    For many programmers coming from an OOPs paradigm, recursion is a familiar thing but quite often that familiarity level goes begging initially. Due to immutable values, the only way to loop over elements is to use recursion. But, once comfortable, it is much more interesting to write codes using recursion.
  • Because you can’t mutate existing data, you instead use a pattern that I call “update as you copy:"
    It is quite easy to mutate existing data in Non-FP, but in FP, what you do is (a) copy an existing object to a new object, and then as a copy of the data is flowing from the old object to the new object, and (b) update any fields you want to change by providing new values for those fields.
  • Pure functions and I/O don’t really mix
  • Using only immutable values and recursion can lead to performance problems, including RAM use and speed:
    But, this can be reduced to a great extent by the use of tail recursion.

Conclusion

FP paradigm is not a replacement of OOP, but it is definitely worth learning as it provides an amazing bunch of features, which we have discussed above. It is quite hard to decide the side to choose in the battle between FP and OOP, but we can surely say that FP certainly holds the upper hand when it comes to concurrency/parallelism, math, etc. I hope now you have a fair idea about FP and are ready to explore the world of functional programming paradigms.

References

  • Learning FP in Scala By Alvin Alexander

How do you break a Monolith into Microservices at Scale? This ebook shows strategies and techniques for building scalable and resilient microservices.

Topics:
java ,immutable values ,functional programming ,fp ,paradigm ,pure functions

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}