Hello there, my name is Oleg Abrazhaev. I have a pretty interesting biography and a career as a Software Engineer for a couple of years. And as many Software Engineers, I haven’t touched functional programming at all, until some time. And when I did, I realized several things.
- OOP and imperative way of writing code is so dominant, that a lot of developers even do not know, that other approaches exist and that there are other ways to write code.
- We use techniques and approaches from a functional programming all the time while writing our OOP code implementations, but we do it unconsciously.
Why I think, that these statements are true? First of all from my very own experience. Not only from writing the code myself but also by watching other developers at work and doing code reviews.
Nowadays, functional features become more and more popular. With rising of tech disciplines such as Data Science, more and more languages adopt functional features.
Indeed, developers still prefer OOP style. They are mostly learning this from universities, from examples in the most popular programming books, which are mostly… in Java.
Let’s talk about, what does it mean to…
Thinking functionally
When a developer has functional programming in his arsenal, now there is a choice which approaches to pick up for solving a specific problem. And usually, we tend to use something which we know the best. Even if it is not the best option. :)
Shift of paradigms
When we pick up OOP, we deal with Classes, Objects relations, like Encapsulation, Inheritance, Polymorphism. We use approaches like SOLID and patterns for organizing our project objects and classes.
When we work with functional programming, we mostly deal with a chained functions calls. Call a function, while calling a function in order to let you call a function while you calling a function. :)
To start really and consciously use functional programming, we need simply start to do it. It is required a shift of paradigms, shifting of the way we are thinking. We all think in some language. For somebody, it is English, for somebody Russian, etc. Language brings some cultural meanings with it. So, we could say, that language which we are using shapes our thoughts.
The same true for the programming language which we use. It determines selected approaches for problem-solving.
And again, there are many ways to implement the same things. We can choose to build a monolith or distributed app, for example.
A recommended way of switching, for example, from Java to Scala, I have described already in my other post.
In this post, I gave an example, how after trying to use Scala first, I started to notice opportunities to use more functional approaches even in Java.
The main point is – I recommend to try some real functional language, that will help. For me this language was Scala.
What makes Scala great functional language?
I want to give a quick overview of the language features.
Scala suits both worlds
First of all, Scala is the very easy language for developers to dive into functional programming. Because with Scala, it is possible to write code in the familiar OOP style, but try functional in implementations. So, a developer can start smoothly. Scala suits both worlds.
On one hand, we still have Classes, like in Java. We can even easily import Java libraries and use them in our code because Scala runs on JVM.
Also, we have differences in OOP implementation.
Objects could be declared, like classes, and they act like singletons. It is useful for creating stateless helpers with some static methods. Objects could act as class companions, providing easy access to some class methods. Could be used as factories.
Case classes are very useful for describing custom immutable data in our model. And because of their immutable nature, they could be easily used in the pattern matching by their properties values.
Traits give developer possibility to not rely only on classes extension. With traits, we can implement aka interfaces, but also use them as mixins, which are almost multiple extend.
Because we can extend from a trait, but also just include its methods in the class with the keyword
with.
On the other hand, Scala has the support of all features, which usually functional programming languages must have.
Immutable data is a preferable way for use in implementations. By default all collections are immutable and it is encouraged to use the keyword
val often, instead of var.
First class functions are called so because in Scala they are actually objects and act as a language first-class entities.
And because of that, we can do effective pure functions composition without side effects. This brings the support of so-called High order functions. A function can be assigned to the value, accepted as a parameter or be returned as a value.
1 2 3 4 5 |
def smartCompare(s1:String, s2:String, cmpFn:(String,String) => Int):Int = { cmpFn(s1,s2) } |
When we don’t have side effects in the functions calls – application easy to parallel.
Unified type system
In Scala, everything is an object, even functions. And types tree has a great organization with an inheritance from top types. Every value in Scala inherits from type Any and every Reference from type AnyRef. It makes possible to bring Type Inference on the board. Basically, in Scala type can be determined from a value or from a context of the declaration. For example:
1 2 3 4 5 6 7 |
scala> val PI = 3.14 PI: Double = 3.14 scala> val maybeImString = "Guess my type" maybeImString: String = Guess my type |
As we can see, concrete type determined from value.
We have Statically typed languages and we have Dynamically typed languages. Let’s talk about both pros and cons.
Statically typed languages examples are Java, C#, C++. A code in this languages is usually verbose because we specify a type in the code. By doing that we give compiler possibility to find and show all errors at compile time. That’s why Statically typed languages are more robust than Dynamically typed.
Dynamically typed languages examples are Python, Javascript, PHP. People who don’t like verbose code prefer these languages. But it has a cost. Because type is not specified, it is known only at a runtime. And because of that, we have lower performance and strange runtime issues. Mostly caused by some type errors.
Scala again takes best from both worlds with Type Inference.
No more NullPointerExceptions
Another big problem of other languages is dealing with
null. Scala suggests a way to avoid null in code at all. Developer shall not return null and shall not test variables on null with if statements.
Special object types should be used instead. They are Option, and two subclasses – Some and None. It is simple to understand. When we are not sure about if the function will return some result, we use Option as a return type. Then inside the function body, we return Some if some result produced or None otherwise. None doesn’t lead to an exception or fail. It means simply emptiness, and will be transformed in an empty string, or zero or completely ignored later. From Some, later, we can extract specific result value.
When we are not sure if function call will be accomplished without error, in the imperative style we use try/catch block. In Scala, we have another group of classes to deal with such situation.
Try, Success and Fail can be used in the same way, as Option, Some, None. A function will return Success with a result or Fail with the error message. And will not lead to the process termination.
Collections
Are immutable by default and have mutable implementations. But it is discouraged to use mutable data. I will not stop here for a long, but will just give a list of some collections features
- Immutable, mutable, arrays, tuples (from 2 to 22 members)
- and the possibility to use map, filter, flatMap, foreach, partition, etc.
- and for multiple elements – scanLeft/Right, foldLeft/Right, reduce
- Easy transform between collections (zip lists, tuples to map, loop)
- tupled, flatten, Nil, ::, :::, head, tail, etc.
Don’t be shy, ask Google for the explanation. ;) By the way, Scala has great online documentation.
Expressions over Statements is a common way in Scala. The difference is that expression returns value. Statements only give a command for a program to do something.
And in Scala, if/else statements are expressions. For loops could be statements, but with the keyword,
yieldthey become expressions and return collections.
If keyword could be used to filter for loop or as a protector in the pattern matching.
Pattern matching is like usual switch in Java, but more powerful. In matching cases, it is possible to use logical expressions and if keyword. Also, we can match the type in Scala, which is useful in many cases. Some examples are matching specific Exception type, or return only needed objects in the collection collector, etc.
But let’s talk a bit more about functions. :)
First class functions
As it was mentioned above, functions are first class objects in Scala. And it is explained, which benefits it gives.
Some more advanced functional features connected with functions in Scala are Closures, Parameter groups and partial application, Currying.
A closure is the same, as in Javascript. Function, even returned from another function, when executed, can still rely on the parent function local scope variables.
1 2 3 4 5 6 7 8 9 |
def getAreaClosure = { val PI = 3.14159 val getArea = (radius:Double) => { PI * radius * radius }:Double getArea } |
The partial application gives the possibility to declare function based on another function with some parameters omitted. In mathematics, there is a term called Arity. An Arity is simply a number of parameters which function has. With the partial application, we reduce Arity of the function.
1 2 3 4 5 6 7 |
val piCompare = smartCompare( "Pi",_:String,compareStrings ) piCompare("Pi") piCompare("Abc") |
Parameters could be grouped. This is called parameter groups. And it looks like that.
1 2 3 4 5 6 7 8 9 10 11 |
def curriedCompare( cmpFn:(String,String) => Int) (s1:String,s2:String): Int = { cmpFn(s1,s2) } //... curriedCompare(comparator)("str1", "str2") |
Currying is a combination of parameters grouping and partial application. We could omit the whole parameter group and create another function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def curriedCompare( cmpFn:(String,String) => Int) (s1:String,s2:String): Int = { cmpFn(s1,s2) } curriedCompare(cmp)(str1, str2) val defaultCompare = curriedCompare(compareStrings) (_:String,_:String) defaultCompare("abc","xyz") |
All techniques showed above are significant for code reuse in a functional programming language.
In OOP we deal with method overload inside inheritance hierarchy. We make code reusable by good OOP design.
In functional programming, the same achieved with functions composition.
How I use Scala at my current work with real-life projects
At my current job at Customer Alliance, Berlin, there are two projects based on Scala.
Functional way is suit most when we have an application with the intensive data flow. So we need to process this data effectively. For that, we want to parallel processes.
Another good indicator is when we do not care about the state in the app. In both cases, immutable data and functions without side effects come in handy.
One of the projects is Data Collector service. It simply collects data from a lot of sources, such as APIs, in different formats. After data processing, it pushes result to the queue, which consumed by the main app PHP consumers and written in the DB. Here is schema representation of the app flow.
Another project is based on the same principle but has interesting differences. Instead of collecting data, this project tries to find data on the Internet. As an input, it has some business definition, like name, address, etc. Then it uses Google geo-services to determine the business better and even find it’s longitude and latitude. Then it searches in Google for this business and takes into consideration only URLs of already integrated into the first project resources. For every URL it tries to match given business with the found one.
It has two queues, input, and output, which makes the process completely asynchronous. As soon as there is a match, it goes to the output queue and consumed by the main app PHP DB writer.
First projects help implement the main business logic. The second one helps to automate internal processes. Both written in Scala and run smoothly in 10-40 processes on the server.
That’s it. I hope I will motivate somebody to consider for use some functional language like Scala for the next project or service.