Many Java developers absolutely love Scala and always prefer it over Java for any kind of tasks: new applications, components of existing ones or even performance-critical elements of existing Java modules. This makes Scala more and more popular, and it’s already made its way to an enterprise world.
Such known companies as Amazon and Blizzard are already using Scala and facilitate their apps with all the benefits it has. It was love from the first sight between Scala and us, and we just want to tell you why.
There are a lot of articles that made it quite clear that although somewhat complex upfront, the functional approach of Scala language multiplied by absolute Java interoperability makes it far superior to plain old Java 6 or Java 7.
But Scala has proved its usefulness to the point when new versions of Java started implementing Scala features, as well as other functional languages. By releasing Java 8, Oracle has caught up with the trend of pumping up conventional imperative OOP languages with functional programming features. This release of Java has incorporated some functional features of Scala that are the most popular among developers, the ones that lessen the routine of trivial operations in developer’s work:
- Support for lambda expressions: no more tons of unnecessary code for common operations like working with collections;
- Support for higher-order functions: make your code more reusable, much less literal and much more self-explanatory;
- Parallel collections: enjoy some benefits of multi-core systems for data processing without any headache;
- Type inference: make code more reusable and less literal, but still only on runtime level, though;
- Virtual extension methods (as opposed to traits in Scala): it allows new behaviors for classes:
If you take a closer look, a lot of approaches that have been introduced in Java 8 are really similar to the ones offered by Scala. But is there still a difference, and should you choose Scala over Java anyway?
Here is our comparison of Scala and Java 8 which we’ve made as a list of distinctive features.
1. Even with Project Lambda, Java 8 is Still a Conventional Imperative Language
Though new features and approaches are surely simplifying developers’ life, it's still quite not the same as functional programming. Java 8 only gives a glimpse of what functional programming has to offer: the most basic stuff.
Still great stuff, nevertheless, it gets praised by beginners in Scala:
“You can do this stuff in one line!”
“You don't have to do that stuff anymore at all!”
“In Java this code would be a clunky disaster, in Scala it is as clear as a sunny day!”
Oracle did a good job of grasping and grabbing most of the hot features that functional approach has to offer, and in many cases implemented them nearly identical to Scala, so no one will ask “Everything was so good in Scala, why Java had to ruin it?”.
Somebody can even think “Well, Java has all the good stuff now, what more can I possibly get from switching to Scala?”.
There is still one big drawback in this viewpoint.
It’s caused by a simple misunderstanding, as Scala often becomes one’s first functional language, and introduced as a set of cool features mentioned above. Plus, Scala makes it possible to write an imperative code, so it is often used and considered as “Java with syntax sugar and a set of cool features”.
And that's wrong.
It is Java 8 that is still a conventional language with functional programming features.
Scala is a full-fledged functional programming language.
And in the next section, we'll try to explain why it makes all the difference, not only for your everyday code, but for the whole world of software development as well.
2. Scala is a Functional Programming Language
And it literally means “write less, do more”.
You are no more just another “construction worker”, with functional paradigm. You're more like an “architect”. We dare say that it's one of the major differences between conventional imperative and functional approaches for a developer.
Imperative programming implies that you write all steps you need in order to achieve the goal.
For example, logic of your application demands to select some string
s that match your criteria from a big list of other string
s. The best way to illustrate imperative approach to this task is to implement this logic in “Old Java” (7, 6 or older) style:
```
List<string> filteredList = new LinkedList<string>();
for (stringToFilter : stringsToFilterList) {
if (StringUtils.isNotBlank(stringToFilter) && stringToFilter.startsWith("somePredicate")) {
filteredList.add(stringToFilter);
}
}
```
So we have three technical steps and only one meaningful operation that has to do something with application logic.
In functional language like Scala, the same application logic implementation will be reduced to the meaningful operation only:
```
val filteredList = stringsToFilterList.filter(s => s.nonEmpty && s.startsWith("somePredicate"))
```
Yup, only meaningful operation that has something to do with application logic left here.
You have just expressed what you really wanted to do, nothing more.
Still, there is an important thing to remember. Nothing will prevent you from literally mapping “Old Java” style code to Scala.
It means that if your mind works like “I want to filter all non-empty strings that start with something meaningful to the app logic”, your code will be nice and elegant, but if it works like “I need to create a storage for filtered data, iterate over initial data, check condition for every piece, store it if condition matches”, you will get code much like our “Old Java” example:
```
val filteredList = ListBuffer[String]()
for (stringToFilter <- stringsToFilterList) {
if(stringToFilter.nonEmpty && stringToFilter.startsWith("somePredicate")) {
filteredList += stringToFilter
}
}
```
Nothing will stop you, and you wouldn’t see why you need a functional programming language at all, it will look like “everything is just the same”. So with Scala (and functional programming in general), it's really “What you think is what you code”.
Functional programming offers many ways to express your algorithm in a way you see the most fit, just like mathematics.
Now, you may say “Wait! But Java 8 is doing things nicer!”, so let's take a look at that as well.
Java 8 has grasped lambda expressions and our example will look smaller:
```
List<string> filteredList = stringsToFilterList.stream().filter
(s -> StringUtils.isNotBlank(s) &&
s.startsWith("somePredicate")).collect(Collectors.toList());
```
It's still not the same. Oracle didn't change an approach at all. It's still our plain old Java with new crutches. They've just tried to cure the symptoms and bring in “these cool features everybody is talking about”, it didn't change an approach at all.
Even though common collection operations in Java 8 are now less literal, it doesn't change the fact that it still a conventional imperative programming language, and in many cases you still have to write lots, lots of code that makes sense only from technical point of view, but not from applications' logic.
Functional programming tries to avoid that. We've stated earlier, that functional programming provides many tools that enable succinct expression, just like math. And that's why it changes not only your everyday code, but software development process itself.
In fact, functional programming is very much about using mathematics to reason about programs. To do so, you need formalism that:
- Describes the programs themselves
- Describes how you can make proofs about properties they might have
And there are many models of computation that provide such formalisms, such as lambda calculus and turing machines (and there's certain degree of equivalency between them). Well, here's the interesting thing about math: the less powerful the formalism is, the easier it is to make proofs with it.
Or, to put it simpler, it's too hard to reason about imperative programs (especially with side-effects like mutability).
As a consequence, there's very little advance regarding concepts in imperative programming.
The famous Design Patterns were not arrived at through study, and they don't have any mathematical backing. Instead, they are the result of years and years of trial and error, and some of them have since proved to be misguided.
And who knows about the other dozens of "design patterns" seen everywhere?
Meanwhile, in functional programming community, things are much more lively.
For example, Haskell programmers came up with Functors, Monads, Co-monads, Zippers, Applicatives, Lenses - dozens of concepts with mathematical backing and, most importantly, actual patterns of how code is composed to make up programs. Things you can use to reason about your program, increase re-usability and improve implementation correctness regarding application logic.
Functional approach provides you with means to write better programs, not better ways to write some steps of your algorithms.
We'll cover some of these means, the ones that are distinct to Scala as a functional programming language, in the following sections.
3. Monads
A monad is a concept that has got to functional programming from math.
They're often called “programmable semicolons”, because semicolons are often used to chain together individual statements in many imperative programming languages (obviously, Java is no exception). Thus, “programmable” semicolon implies that between the statements, some extra code will be executed.
It is a structure that represents computations defined as sequences of steps: a type with a monad structure defines what it means to chain operations, or nest functions of that type together.
This allows the programmer to build pipelines that process data in steps, in which each action is decorated with additional processing rules provided by the monad.
By definition, monads are perfect for chaining stuff together, and Scala makes great use of it.
In this article, we won't cover either formal definition of a monad or what conditions must be met for something to be called a real monad.
We'll talk about benefits of monads in programming and how this concept was grasped and handled by Scala.
Among beginners, the most popular monads in Scala are:
1. “Maybe”M monad. “Maybe” monads are often referred to as “Optional Types”.
In Scala, maybe monad implemented as Option[T]
class, which can be in two definite states: Some[T]
(when some actual value of type T
is stored inside) and None
, when it stores nothing.
You can always wrap something in Option
with Scala:
```
val maybeString = Option(someOtherString)
```
In Java, you would need to always check if there is an actual value inside by yourself, but in Scala, Option
does this for you, and thanks to immutability, you don't need to check this again and again.
Scala provides quite handy helper methods for "maybe" monad:
```
maybeString match {
case Some(s) => {
case None => {
}
maybeString.getOrElse("defaultValueOfThisString")
maybeString.orElse({
log.warn("Using default value!!!")
Some("defaultValueOfThisString")
}).get
if (maybeString.isEmpty) { ... }
if (maybeString.exists(_.startsWith("somePredicate"))) { ... }
```
So convenient! We bet you've already imagined the possibilities this brings to your code!
Maybe monad is pretty easy to implement, that's why it's pretty popular to adopt nowadays (check this Swift vs. Objective-C article), among other functional programming features, but for some reason Java 8 still hasn't incorporated it.
2. “Try”. Strictly speaking, this is not a monad, because it doesn't meet one of the formal conditions. Nevertheless, it behaves much like one and you can view it as a substitution for the usual try
/catch
routine.
```
Try({
})
...
Try(SomeClass.someMethod())
...
```
Ok, now we get to the good part. Try
also has two distinct states: Success
, when everything went fine, and Failure
, for example when exception has occurred OR result doesn't meet some conditions of yours.
Let's take a look:
```
Try(SomeClass.someMethod()) match {
case Success(s) => {
case Failure(e) => {
}
Try(SomeClass.someMethod()).recover({
Try(SomeClass.someMethod()).filter({
Try(SomeClass.someMethod()).toOption
Try(SomeClass.someMethod()).getOrElse("somethingDefault")
Try(SomeClass.someMethod()).orElse({
log.error("We couldn't get that thing for you, enjoy your default value")
Success("somethingDefault")
}).get
Try(SomeClass.someMethod()).isSuccess
Try(SomeClass.someMethod()).isFailure
```
And, as usual with monads, you could combine all of the above as your needs dictate.
The usual try
/ catch
routine pales compared to this. You can't be this flexible about your application workflow in Java, or other imperative languages, only functional approach allows this.
And yes, if you want a simple try
/ catch
for some reason, it is still available to you.
3. “Future”. This is the most interesting one, as it opens the whole new world for beginner developer asynchronous programming.
Scala's concurrency model makes a good use of this monad, and it's what makes it so powerful and robust. You can now make bits of your code independent and non-blocking, and you still can block and wait for result. How cool is that?
```
val futureResponseBody: String = future {
}
println("Yay! Wasn't blocked by request!")
```
Now, how we can handle it?
```
futureResponseBody onComplete {
case Success(responseBody) => {
case Failure(e) => {
}
futureResponseBody.map(body =>
Await.result(futureResponseBody, 30 seconds)
futureResponseBody onSuccess { ... }
futureResponseBody onFailure { ... }
futureResponseBody.isCompleted
futureResponseBody.value
```
As you see, monads in Scala comply to a shared set of rules and can easily be chained and converted to each other as per the convenience of the developer.
Monads in Scala enabled approaches that made traditionally tedious things fast and robust.
And again, this allows you to concentrate on implementing logic of your application and not to waste time on purely technical stuff.
4. Immutability
Immutability is what enables functional programming in first place, and as we try to illustrate, functional approach makes all the difference in software developers' experience.
Immutability is a paradigm by which no object can change its state after creation. If you need to change immutable objects state, you create a copy and change the required properties.
The easiest example of an immutable type for Java developer is String
. Every Java developer knows that in Java, string
s are immutable, and for every change a copy is created.
Immutability is a key to functional programming because it matches the goals of minimizing the parts that change, making it easier to reason about those parts.
So what's so good about that, anyway?
- No side effects. You don't have to worry that something will change an object you've passed somewhere. But why is that good? Keep reading.
- Execution speed up. With immutable objects, success or failure based on mutability is forever resolved once the object is constructed. That means less checks in runtime and more optimization from compiler, which gives faster execution of your code as a result.
- 100% Thread-safety. You don't have to worry about locking because shared objects can never change. You don't have to think through the code and figure out all the places where two threads might try to change the same object at the same time, and you don't have to write more code to prevent the problems this might cause.
- Cleaner code. The very fact of immutability of the object will make the code that makes use of it much, much cleaner and readable.
But what's so good about immutables from Java development's standpoint?
Immutables make a whole bunch of normally troublesome things in Java go away:
- By isolating the places where changes occur by severely restricting mutation, you create a much smaller space for errors to occur and have fewer places to test.
- Immutable object can never exist in unknown or undesirable state because of an exception. All initialization occurs at construction time, which is atomic in Java, so any exceptions occur before you have an object instance.
- As stated above, immutable objects are also automatically thread-safe and have no synchronization issues. And it's a huge benefit for Java concurrency model. If no object methods can modify its state, no matter how many of them and how often are being called parallel, they will work in their own memory space in stack.
- Because changes only occur upon construction, immutable classes make it trivial to write unit tests. You do not need a copy constructor, and you need never sweat the gory details of implementing a
clone()
method. - It's trite to say but immutable objects make good candidates for use as keys in either
Maps
or Sets
. Keys in dictionary collections in Java cannot change value while being used as a key, so immutable objects make great keys.
Ok, but what exactly does Scala handle better, to the point that you’d want to use it instead of Java?
Java's approach: Everything is mutable until specifically made otherwise, or in simple words: “You can make something immutable if you want, but how would you know that you need to?”.
In many cases, nobody bothers to do so. Yes, there are best practices and guidelines available, but developers have too much on their hands just dealing with everyday tasks and often have no time to explore and learn new features and best practices for programming language they use (despite the fact that it's exactly what they need to do the most).
Consider that:
1. You have to know how to make something immutable in Java and to have an interest in doing so, it's not as simple as one keyword:
- All fields of the class must be
final
. - Class must have no mutating methods other than the constructor.
- Class must be
final
so it cannot be overridden. - Which figures no default no-argument constructor for you.
- Which figures you should have a constructor that inits all the fields.
- Which figures you may need helper entities (maybe some DTOs), which should be immutable too.
From Java developer point of view, this already looks bothersome, especially when you realize that in result your class will be lone immutable in the sea of the mutables.
2. Minimization of production time is always a top priority in software development, and as you see, going with immutables in Java takes time and effort;
3. Enterprise software can always grow extensive due to ever lowering cost of machine time, so such an effort in existing project may cost more than additional server or two.
And you get that most of the senior developers don't bother with immutables, and junior developers don't care and just copycat code already present in project.
Scala's approach is different: Use immutable objects until you clearly need something to be mutable. While technically, you should still explicitly state mutability/immutability of an object, as a functional programming language Scala makes use of immutables by default wherever it's technically possible and tries to set programmers mind this way all the time, even with compiler warnings (“Hey, this thing could be immutable!”).
Okay, but how can this Scala approach will help an existing Java Project? The answer is: Easy.
Scala has full Java interoperability, so you can write a component, module, or just a single class in Scala and use it from your Java code, as well as you can call any Java class from Scala code.
So with Scala, you have all benefits of immutability without setbacks of its integration into conventional imperative programming language (where mutability is default), in your already existing project.
How cool is that?
5. Safe and Simple Concurrency Out Of the Box
Java 8 standard library provides only traditional thread-model concurrency.
By this model, the execution of the program is split up into concurrently running tasks.
This is similar to running multiple copies of one program in shared memory pool.
Problems with threads and traditional concurrency approach are well known:
- Thread creation is a pretty heavy operation by itself.
- Communication between threads is a headache.
- In general, it's difficult for a developer to get benefits from threading because of problems it brings, especially with mutable types. Concurrent modification, thread starvation, dead locks, you name it.
Traditional threading is opposed by much more simple and efficient actor model, which Scala standard library includes support for.
The actor model takes a different approach to concurrency, which should avoid the problems caused by threading and locking. As the name implies, this model defines each object as an actor, an entity that has a mailbox and a behavior.
This takes care of:
- Thread communication. Messages are easily exchanged between actors via their mailboxes.
- Thread control. For each message, a custom behavior can be defined. This includes things like sending a number of messages to other actors, create a number of actors and assume new behavior for the next message to be received;
- As the whole communication is done by messages, there is no shared state between actors, so there are no problems tied with that.
Scala also to some extent eliminates headache with locking thanks to its immutability approach.
Scala standard library includes support for the actor model out of the box. They are available through the scala.actors
library.
Actors implementation is a great testament for the expressiveness of Scala: all functionality, operators and other language constructs included, is implemented in pure Scala, as a library, without requiring changes to Scala itself. Let's take a closer look.
Scala makes the distinction between thread-based and event-based actors.
Thread-based actors are each run in their own JVM thread. They are scheduled by the Java thread scheduler, which uses a preemptive priority-based scheduler.
When the actor enters a receive block, the thread is blocked until messages arrive. Thread-based actors make it possible to do long-running computations, or blocking I/O operations inside actors, without hindering the execution of other actors.
Event-based actors get rid of another traditional threading problem - huge overhead of threading which greatly limits amount of threads that can be created, thus, greatly reducing benefits from multi-threading in the first place.
So, how exactly are event-based actors different?
Event-based actors are not implemented by means of one thread per actor, yet instead they run on the same thread. An actor that waits for a message to be received is not represented by a blocked thread, but by a closure. This closure captures the state of the actor, such that its computation can be continued upon receiving a message. The execution of this closure happens on the thread of the sender.
This way event-based actors make a more light-weight alternative to thread-based actors, allowing for a large number of concurrently running actors (like, 100 000 of actors on a home-level machine). Event-based actors, though, bad for parallelism: since all actors execute on the same thread, there is no scheduling fairness.
To use event-based actor in Scala you should use a "react
" block instead of a "receive
" block in your actor.
There is also one big limitation to using event-based actors: upon entering a “react
” block, the control flow can never return to the enclosing actor.
Scala allows programmers to mix thread-based actors and event-based actors in the same program, so you can use either scalable, lightweight event-based actors, or thread-based actors that allow for parallelism, depending on what you need.
To make something an actor in Scala, you simply extend Actor
trait and implement receive
or react
block:
```
class SampleActor extends Actor with ActorLogging {
def receive = {
...
}
}
```
Creating a new actor
is also very simple:
```
val sampleActorSystem = ActorSystem("SampleActorSystem")
val sampleActor = system.actorOf(Props[SampleActor], name = "sampleActor")
```
Sending a message is also a no-brainer:
```
sampleActor ! SampleMessage("It's a test message")
```
And defining a behaviour for this message is also quite simple:
```
class SampleActor extends Actor with ActorLogging {
def receive = {
case SampleMessage(msg) = log.info(f"Got sample message: $msg")
}
}
```
And if for some reason, you want to use plain old Java concurrency, you can use that too.
Take this simplicity and flexibility, add benefits of using immutable types by default and you get that Scala's concurrency model is much, much superior to Java 8.