DiversIT Europe
Previous Home Next Nov 20, 2017 Scala is not difficult Tags: scala

Six years ago I learned about Scala when a customer decided to switch from Java to Scala. It was ‘love at first site’. First because Scala allows you to write more readable code since all boilerplate code can be left out. But also because Scala offers so much more possibilities compared to Java because it supports both OO and FP concepts.

The default position of many Java developers, at least in The Netherlands, towards Scala seems to be that ‘Scala is difficult’. In The Netherlands a strange situation seems to occur because customers do want to use Scala, but cannot find the developers, because the Java developers do not want to learn Scala. With the current speed of technological advancements you cannot stay in your Java/Spring/Hibernate comfort zone. You have to look further to find the best solution for your customer to reach its goals.

(Note: this article was published in the Dutch Java Magazine (in Dutch))

Java development

The development of Java over the last decade has been slow while this last decade has been full of amazing technological enhancements. Twenty years ago no one had even thought about ‘cloud deployment’, non-blocking microservices, concurrency and multi-core processors. Java does not offer the developers the tools and libraries to help with these new concepts.

Unlike Java, Scala, and other emerged JVM languages, do help you as a developer. Because of it’s excellent compatibility of Java, you should not see Scala as a different language, but as a futuristic version of Java which is already available to you now and which does allow you, with a similar syntax and with the reuse of your current Java codebase, to develop the applications which are requested at this moment.

Java’s newly proposed release scheme does not mean Java’s language development will now suddenly go much faster. Overall, in about 3 years time, the same amount of features will be released. It only means small features can be introduced much quicker and big features can be added incrementally (like project Amber).
Sure, you can wait for Java to eventually support features like data class, value class, pattern matching, var (Java can never support val! See ‘Ask the architects’ @ Devoxx BE 2016), etc. But why wait? These feature are already available to you in Scala! Why are they not acceptable now, but are acceptable when part of Java?

Scala is often seen as ‘difficult’ and ‘complex’ because the language offers so much possibilities, or because people tend to use difficult mathematical terms because of its Functional Programming capabilities. Also, most courses in the early days seemed to be targeted more towards Scala’s FP and mathematical aspects instead of targeting the OO/Java developer.

Too difficult?

However, Scala does not have to be difficult at all! Scala’s language specification, for example, is a lot smaller then Java’s, while still supporting all its features, and more. Just by using some of Scala’s features, you, the Java developer, can be much more productive.

Rod Johnson once said during a ScalaDays keynote that he thought there will be 2 kinds of Scala developers: those which use Scala pragmatically to build Java-like applications with less code, more readable code and less bugs. And those who go wild on Scala to create fantastic generic libraries for the first group. I can relate to this.

I belong to the first group and on my current project, for the last 3 years, we have used Scala this way. Still, I find it intriguing to see and learn how the second group uses Scala more functional. This knowledge does come back in our production code, but only if we think it makes the code even simpler and more readable. Of course it is possible to write unreadable Scala code, as can be done in any language, but good Scala code is more readable than Java code. It is the responsibility of the team, by doing reviews, to guard the code stays readable and understandable.

Handy features

Examples say more than a 1000 arguments. So here, and in next blogposts, I will give examples of how Scala features can help you to write better code. First, forget about Functional Programming or Category Theory!. You do not need this to become a more productive developer.

REPL and worksheet

With Scala’s REPL and IDE Worksheets you can quickly try out some code. And of course you can also use Java in there (who needs JShell!?). After the installation of Scala on your local machine, start the REPL by typing ‘scala’ on the commandline. All examples can be tried out using the REPL.

Case classes

Reduce your POJO to a single expression using a case class. Methods like ‘toString’ and ‘copy’ and a correctly implemented equals and hashCode you get for free. With the case class you reduce your Hibernate entity or DTO to just a few lines.

> case class User(firstName: String, lastName: String, age: Int)
defined class User

Named arguments

When using a method, the name of the argument can be used to make more clear in code to what you assign a value. And you can even use the arguments in a different order then they are defined.

> new User(lastName = "Klaassen", firstName = "Jan", age = 22)
res1: User(Jan, Klaassen, 22)

Default arguments

By providing a default value for a method argument, the argument becomes optional. The argument gets the default value if the argument is not being provided when calling the method. Therefore it is no longer necessary to define all kind of overloaded methods which make you code must simpler and smaller.

> def add(x: Int, y: Int = 2) = x + y
add: (x: Int, y: Int)Int
> add(1)
res2: Int = 3

When the add method is used, only the first (x) argument has been provided. The y argument gets the default value 2.

Type inference

Scala offers a better type inference so you can leave out the often redundant type to make the code more readable. E.g. with variables or return types.

> val user = User("Jan", "Klaassen", 31)
user: User = User(Jan, Klaassen, 31)

The variable ‘user’ automatically gets the type ‘User’ because the compiler can infer this type. No need to specify it. Of course, you can specify it if you want. And you should use it if it makes the code more understandable. E.g. always specify the return type for public methods.

No null’s, NullPointerException’s and checked exceptions.

null is the billion dollar mistake! So why is it still used so often?
By using return types which clearly indicate a good and bad state, it is no longer needed to use null, or exceptions! No more runtime NPEs! And, this makes the code much more clear for the developer, because the type now shows all possible outcomes the developer can choose how to handle each possible value. Scala offers several of these types like Option, Try, Either and Future. Libraries like Scalactic, Scalaz or Cats also provide types like ‘Or’ and ‘Validation’.

> trait Repository {
    def findUser(id: String): Option[User]
    def updateUser(user: User): Try[User]
    def updateAsync(user: User): Future[User]
}

Note that this practice not only applies to Scala, but can be applied to any language, also Java!

String interpolation

String interpolation offers a much simpler way of formatting a String keeping the String format clearly readable. A String is turned into an interpolated String by prefixing it with s.

> s"User name ${user.firstName}"
res4: User name Jan

Raw strings

When using a raw string you do not have to escape characters anymore. Again making the code more readable. This is ideal for regular expressions and json content. But also for multi-line strings. And, raw strings can be combined with string interpolation.

> val json = s"""{ "name": "Jan", "age": ${user.age} }"""
json: String = { "name": "Jan", "age": 31 }

In this raw string, the double quotes do not need escaping anymore. The user.age is filled in by using String interpolation (the s prefix before the tripple quotes).

Or, use a multi-line String if the json is too long, or to make the core more readable.

> val json = s"""
     | {
     |   "name": "Jan",
     |   "age" : 10
     | }""".stripMargin
json: String =
"
{
 "name": "Jan",
 "age" : 10
}"

By default a | is used to mark each line, which is removed by the stripMargin function, but any character can be used.

Regular expressions

… become a lot simpler using raw strings. Adding .r transforms a String into a Regex object.

val timeRegex = """(\d+):(\d+)""".r
timeRegex: scala.util.Regex = (\d+):(\d+)

If you use a site like debuggex.com for testing your regex, you can now just copy paste the regex into a raw String without having to add escapes like in Java.

Pattern matching

Scala’s match is an advanced switch expression in which also objects and regular expressions can be used. Objects can directly be ‘extracted’ to assign property values to variables.

> def switch[A](a: A) = a match {
  case User(fn, ln, _) => s"User with name $fn $ln"
  case timeRegex(hour, minute) => s"$hour hour $minute"
  case other =" s"Unknown $other"
}
switch: [A](a: A)String

> switch(user)
res5: String = User with name Jan Klaassen
> switch("10:14")
res6: String = 10 hour 14
> switch(10)
res7: Unknown 10

When a is a User, the User properties get extracted and assigned to variables which can then be used in the case function. Any property your not interested in, can be ignored by using _.
When a matches the regular expression, it will extract values for the regex groups and assign those values to the variables hour and minute. When a does not match a User or regular expression, then the default case, which assigns any value to the variable other, is used.

Note that match will try to verify whether all possible cases have been defined. If not, the compiler will show a warning.

Lambdas and collection API

Since Java 8 developers have been introduced into streams and lambda functions. Scala’s collection API makes streams and lambdas even simpler: .stream() and .collect(..) not not needed, and the api defines much more functions so you do not have to implement them yourself like permutations, mkString or zipWithIndex (See Scala’s List api). Also, Scala uses by default immutable collection types. However, the api does contain mutable types as well.

> val adultFirstNames = users.filter(_.age > 18).map(_.firstName)
adultFirstNames: List[String] = List(..)

> val list = List(1,2,3)
list.permutations.mkString(" ")
res8: String = List(1, 2, 3) List(1, 3, 2) List(2, 1, 3) List(2, 3, 1) List(3, 1, 2) List(3, 2, 1)

> val zipped = list.zipWithIndex
zipped: List[(Int, Int)] = List((1,0), (2,1), (3,2))

The collection api also contains lazy collections (views) and parallel collections.

Using scala.collection.JavaConverters Java collections can easily be converted into Scala collections, and back.

> import collection.JavaConverters._
import collection.JavaConverters._

> val javaList = List(1,2,3).asJava
javaList: java.util.List[Int] = [1, 2, 3]

> val scalaList = javaList.asScala
scalaList: scala.collection.mutable.Buffer[Int] = Buffer(1, 2, 3)

Note that Java collection instances are transformed into mutable Scala collection types! The conversion will wrap the instance so there is no performance penalty.

Singleton

Scala has native support for true singleton objects with the keyword object. Each case class automatically gets it’s own singleton object which is called the companion object. Via this object a new instance of a case class can be created without using the ‘new’ keyword. But of course you can also create your own singleton objects. When using from Java, singleton methods are just static methods.

> object UserPrinter {
  def print(user: User) = println(s"User ${user.firstName}")
}
defined object UserPrinter

> UserPrinter.print(user)
User Jan

Call-by-name

A call-by-name argument is not directly evaluated at the call site, but only when used within the method. This allows lazy evaluation of arguments. This is really useful when passing ‘expensive’ objects which might be not used. It’s syntactic sugar for passing a Function[Unit, T] (or a Supplier[T] in Java 8).

> def print(b: Boolean, one: => Unit, two: Unit) = if (b) one else two
print(b: Boolean, one: => Unit, two: => Unit)Unit

> print(true, println("one"), println("two"))
one

Type alias

Using a type alias a complex type can be changed into a more understandable type to make the code easier to read.

> type QueryResult[A] = Future[Or[A, String]]
defined type alias QueryResult

> def find(id: Int): QueryResult[User] = ???
find: (id: Int)QueryResult[User]

Tuples

Sometimes you just want to group some values together without having to create a class for it. E.g. for return types or collection values. A tuple can contain any type.

> def returnsTuple() = (1, "some string", true)
returnsTuple: (Int, String, Boolean) = (1, "some string", true)

A tuple is rarely assigned to a variable. Instead, the tuple is extracted directly using pattern matching.

> val (i, s, b) = returnsTuple
i: Int = 1
s: String = some strings
b: Boolean = true

Conclusion

These are just some of the Scala features which could already help developers to write better and simpler code. Over time, I will post more examples of Scala features on my blog.

Scala already exists for over 12 years. There are many resources available to learn Scala, both offline and online:

  • The Twitter Scala School is still one of my favorites.
  • Scala exercises is a fun project to learn Scala, or a library, by using it via small exercises. The project is open source so you can also run it locally, or within a company, or/and create your own exercises.
  • Scala center is the team which currently works on Scala and also provides many learning resources like the courses on Coursera.

Or take a Scala course online like on Coursera or IRL like the course I teach via Vijfhart (Netherlands). And, of course, you can learn about Scala on the many Scala meetups (I organise the Brabant.Scala meetup), and conferences.

Rod Johnson already said it in 2013: “Scala is my most loved language since C”.
Step out of your comfort zone and give Scala a try!