We all have gone through so many blogs, written by very knowledgable people, explaining why functional programming approach is so good. I will not be adding anything new here. I am just trying to state my experience and what effected my style of work. I started functional programming last September using Scala. Its going to be six month now (Feb 2017) and I feel, that I have learnt more about computer science in the last six month, than in my previous OOPs life.
I was surprised to see that avoiding mutable objects can clean my code so much. Just to take a decision based on state makes code so much ugly. And when you have to do this with tens of mutable states, then its just horrible and you end up with a dirty code that is already ready for refactor. After spending some time in developing data streaming and processing infrastructure, I have learnt one thing, that is, mutable states adds moving parts in your code, which increase complexity. Also using immutable objects, improves performance, as Scala can efficiently use its fast cacheing with immutable objects. Now I try to use only immutable data and transform it to another form as per requirement. For someone new to functional, it may seem very hard, not to use mutable objects, but once they get used to it, they will notice that it brings a lot of simplicity to the programming logic. If you like math you will agree with this. In Mathematics any algebraic equation there is one base rule, that is you can never change a true relative value of a variable. It can be substituted with other expression or term but the value it represent never changes. Below example can be very basic but it gives an idea, that the value of 'a' will never change.
a + 2 = 3
Functions can serve as arguments and results of functions. Ability to pass functions as parameter to another functions makes your code so much clean, short and powerful. I learnt it while dealing with Apache Spark's RDDs. It so easy to deal with complexity when you have power to compose functions dynamically during run time. You can compose functions and filters, then pass your data through it to transform. As these operations are composed from small and specific functions, it is easy to write them and test them separately, giving us more control. For example if there are different type of operations or transformations required on a data set then using partial functions, you can easily create a chain of operations and then box them in one partial function. Now this function can be used to map and transform a collection of data or can be passed as a filter function to filter the collection. Now when you have to write test for so many operations and transformations, it becomes so easy as you already have written test for all the functions. After it is converted to a single function, unit test becomes even smaller and specific for a particular condition. Doing this will require very less code in Scala and each function can be easily tested. This will guarantee accuracy. If the same implemented in imperative style in Java, the code will be very lengthy and will be very hard to implement and to write test for. This is where you will have to introduce various design patterns to get control of the complexity. Functional languages are extremely expressive. In a functional language one does not need design patterns because the language is likely so high level. You end up programming in concepts that eliminates design patterns all together.
Pattern matching is not something new. It has been used and implemented in functional world since the beginning. Imperative programming still don't have it yet fully. What is the benefit? The compiler implements branching for us. This helps us to write operations for different types without the messy nest of conditions. When the function is called, the compiler compares the arguments with the definitions at runtime, and picks the correct one. This is usually done by picking the most specific definition available. And also every time you have a complex structure of nested ifs, pattern matching can do a better job with less code on your part. Another benefit of pattern matching is that if you need to add or modify conditions, you don't have to go into one huge function. You simply add or modify appropriate definitions. This eliminates the need for a whole range of design patterns. And did I say less code?
Concurrency implementation in functional programming is much easier. 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, not even by the same thread. If your application is inherently single threaded the compiler can still optimize functional programs to run on multiple CPUs.
scala> val v = Vector.range(0, 10) v: scala.collection.immutable.Vector[Int] = Vector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) scala> v.foreach(print) 0123456789 scala> v.par.foreach(print) 5678901234
In a functional language the compiler could analyze the code, classify the functions that create potentially time consuming operations, and run them concurrently. Scala collection support this with parallel functions par, which created parallel sequence. This makes it so good for micro services architecture and parallel computing.
When I was programming in java I used to spend so much of my time and code to keep null out of my code. I personally, don't like null because of its ambiguity. What null exactly mean? Is it empty or not available? The answer to question will change from programmer to programer and from project to project. This ambiguity troubles me. If you are not the owner of the code, you always have to dive in to the code and see why null. And then you also have to write many lines of code extra, just to keep the meaning of that null consistent through out the software. Why null is bad, has been discussed a lot. I will not duplicate those efforts. Here is a good read why null is bad.
In Functional paradigm functions are not dependent on any state. Means a method working never changes based on any changing value. It will always perform a fixed set of operations in a fixed order on a particular input. This makes it easer to write unit test against. One has to only test the function operation not the various condition in which this value will be called. You just need to know what input you will pass and what output to expect. As the biggest benefit of Functional programming is brevity so your unit test for Functional code will be small too. In functional programming you will always be able to reproduce your problem because a bug in a functional program doesn't depend on unrelated code paths that were executed before it. It is easy to know which part didn't worked the way it should have. A good functional code doesn't have side effects in the method. So you don't have to write test for side effects. At some point I felt that no side effect make things harder for logging exception. Then again I was new and looking at it with my imperative glasses on. The way exceptions are handled in functional world can be more cleaner, testable and reusable.