5 things that developers do all the time, in Scala

May 17 · 8 min read

Scala is hard. Scala has a steep learning curve. That’s the kind of things that you can read, or that you may think. I strongly disagree. Granted, there are things that are complicated in Scala. But for the overwhelming majority of tasks that a developer has to do in its day-to-day job, it is as easy as, if not easier than, for other languages.

In order to show you a token of the fact that Scala is easy, we present below possible implementations of five common tasks that you find yourself doing a lot as a developer. As Scala can compile to both the JVM and to JavaScript (JS), some of these examples will be “backend” oriented, others will be “frontend” oriented, and some of them even “cross-compile”, as the same code works for both.

If you’ve tried Scala before, or have some passive knowledge of it, you might have heard scary words such as monads, type variance, laziness… These are the kind of things that you clearly don’t need to understand (nor even know their existence) in order to code in Scala. And we will willingly avoid mentioning them below, since it is irrelevant.

The examples that we are going to discuss are

  • making HTTP requests (JVM + JS)
  • serialize into and deserialize from JSONs (JVM + JS)
  • making a bar chart with `D3.js` (JS)
  • making SQL queries using an ORM (JVM)
  • making a small HTTP server (JVM)

HTTP requests

The first example that we are going to look at is making HTTP requests. We will use the RösHTTP library. It has the advantage of being extremely simple and to work with, both on the JVM and in JS. We can for example query the README.md content of that project, that we would get at the following address: `https://raw.githubusercontent.com/hmil/RosHTTP/master/README.md`.

Before being sent, requests need to be prepared by creating an `HttpRequest` object. Making a request object for the above mentioned URL can be done as follows

(The `val` keyword is like `const` is JavaScript.) Another way to prepare the request is to create an empty request, and fill it with information we need.

This is a bit longer, but it however has advantages. First, each method call creates a new request, instead of modifying it. This means that you can prepare a boilerplate request, for example with the host, protocol, and some headers, and fill it with information depending on the context. This enables code reuse. Secondly, it is needed for adding headers and much easier for adding query parameters.

Now we need to send it. This is done by calling the `send` method on the request. So, for example,

The `response` object is now a `Future[SimpleHttpResponse]` (read “`Future` of `SimpleHttpResponse`”). A `Future` is a little bit like a `Promise`. It is an object that, at some point in the future, will contain a value. So we need to do something with it when it is completed. For example, we could do a little bit of data science and count the number of “words” in the page (everybody knows that data science starts with word counting). This is done as follows.

Above, the method `map` takes the result of the Future and creates a new Future by transforming the result with the given function. We first retrieve the body of the response, then we create an array containing all the space and line separated portions of the body, and we then look at the length of this array (the implementation of this word count is obviously not correct, but that’s not the point).

Once we have finished massaging the response, we put a callback with the `onComplete` method, that will do things depending on whether the request was successful. If the request is not, perhaps because of a timeout, then we throw the exception.

Of course, all of that is non-blocking and, if used on the JVM, the callback will be executed in a dedicated thread.

JSON (de)serialization

Another task that needs to be done everyday, both in backend and in frontend, is parsing and creating JSONs. We are going to use the µPickle library. Again, it’s an easy to use solution for serializing and deserializing JSONs. It is also typed, meaning that you start from and you go to Scala classes.

There are essentially two functions that we use:

  • `write[T](object: T): String`
  • `read[T](json: String): T`

that respectively serialize an object of type `T` into a `String`, and deserialize a `String` back into an object of type `T`. In order to be able to do so, µPickle requires us to provide it a `ReadWriter[T]`. But we don’t need to actually understand what it is, or how it works, as we always need to write the same code.

Let’s imagine that we have two classes, `Person` with a name and an age, and `Group`, which has a name and a list of `Person`s. In Scala, we use case classes to represent such data:

case class Person(name: String, age: Int)
case class Group(name: String, persons: Seq[Person])

Both classes need to tell µPickle how to serialize and deserialize themselves. This is done by this piece of code

What this exactly does, I’m not sure I know. All I need to know is that I can now use `write` and `read` functions anywhere in my project. For example, to serialize, we can do

We can also “pretty-print” by specifying an indent, if we prefer. Like so: `write(object, indent = 2)`.

In order to deserialize, we can call `read`, except in this case we need to specify what we want to read:

val group = read[Group]("""{"name":"Some Group","persons":[{"name":"Antoine","age":27},{"name":"Souad","age":29}]}"""))

The `[Group]` in the snippet says that we want to deserialize to an object of type `Group`.

In case we try to deserialize a string that is not valid, we also receive useful feedback:

read[Group]("""{"persons":[{"name":"Antoine","age":27},{"name":"Souad","age":29}]}""")

will lead to

upickle.core.AbortException: missing keys in dictionary: name at index 66

Of course, this is only at runtime, but it is as good as it can be.

You could regret the fact that we need to know the type of the information contained in the JSON when deserializing it. In most scenario though, we know the type of information contained, since deserializing JSON is often the result of either an HTTP request, or reading a configuration file. But, for these few cases where we actually can’t guess what we are going to get, we can use uJson.

We obviously lose all type-safety, but we can’t have our cake and eat it too.

Before going to the next section, we can take a moment to appreciate the fact that this code works both when compiling to the JVM and to JS, which is pretty powerful!

Using D3.js

When compiling to JS, Scala has full interoperability with JavaScript libraries. For example, we can use d3.js, a popular data-driven library for manipulating the DOM.

In this blog post, we can find the code for making a simple bar chart with d3. The code is written below:

We can do the same in Scala. The advantage is that we gain type safety, at the cost of needing to write “facade” types. These types, which are similar to description files in TypeScript, tell Scala what the library contains, and what you can do with it. Recently, facades for basically all JavaScript libraries have been generated, and are available here. We can use the one for d3. When we do, the above JavaScript code is translated in Scala below.

As you can see, the code looks very much the same, with a few notable differences:

  • JavaScript arrays are defined using the `js.Array` constructor (there is no special syntax in Scala for building collections — there’s just too many of them for that!),
  • we don’t need d3’s `max` function, as Scala automatically puts this method for collections whose elements have an ordering,
  • the function `append` is replaced by `append_div`, which is a type safe alternative (`append(“div”)` would also be possible), and
  • we have to specify the type of the function that the `style` function asks.

This can be seen as a token of how easy it is to make a bar chart with d3, but also that it’s quite simple to use JavaScript libraries with Scala. The price to pay for having access from Scala is quite low, but the reward in the long run is not negligible.

SQL queries with Slick

One of the strong suit of Scala is that libraries make a common effort to be consistent with each other. Methods like `map`, `filter`, `flatMap` are ubiquitous. The SQL ORM Slick is no exception. Querying databases will involve these methods. After all, an SQL table can be seen as a collection of `Row`s where the type `Row` defines the columns (fields) of that table. In Slick, it is exactly that.

For example, imagine you have a table `Persons` with two columns `Name` and `Age`. We can represent a row of that table using the class `Person` that we already used above:

case class Person(name: String, age: Int)

Once that is done, we need to tell Slick the existence of that table. This is done with the following piece of code.

In order to query the database, we now create a `TableQuery`

val query = TableQuery[Persons]

And the last thing to do, of course, is to make the connection to the database. For example, we could use an SQLite database. We create a connection with

val db = Database.forURL(
url = "jdbc:sqlite:path/to/db/file.db",
driver = "org.sqlite.JDBC"
)

We’re all set! Now we can start populate the database, like this:

db.run(
query ++= Seq(
Person("Antoine", 27),
Person("Souad", 29),
Person("John", 22)
)
)

And we can, for example, get the names of all those persons whose age is bigger than, or equal to, 25:

db.run(
query
.filter(person => person.age >= 25)
.map(person => person.name)
)
)

As you might expect, querying the database is non-blocking and the `db.run` function actually returns `Future[T]` instead of `T` (where the type `T` depends on the query). We could thus do something like

Again, one nice aspect about all this is that the piece of code

query
.filter(person => person.age >= 25)
.map(person => person.name)

would work if `query` was a `List[Person]`, a `Set[Person]`, a `Future[Person]` or, in this case, a `TableQuery[Persons]`…

HTTP server

The last example is making an HTTP server. A good choice for getting started is cask. Within a few lines of code, we have a REST based server going. Cask is a nice little framework that, for most usages, is a perfect fit, given its simplicity and flexibility. If you look for a war machine instead, you should give play a try.

The way cask works is easy. You write functions that serves as HTTP endpoints. And you “decorate” them to specify to which endpoint they must be attached. For example, we are going to respond “hello” when you query the “/hi” endpoint. The function would be written as

Any time a client will make a GET request to the `/hi` path, it will receive `hello` in the body of the response. Now we simply need to wrap this into a `cask.Routes` object, and initialise it.

And that is it. To go further, you should head over the docs.

Conclusion

After reading all this, you might find yourself thinking “all right, Scala might not be that hard” and that would already be great. But you might also be thinking “but what’s the point of learning something else when I already have a tool that I master”. Well, Scala has other advantages that, in my opinion, push it above these contenders.

First, it is strongly typed, which means that most errors are caught at compile-time, rather than at runtime (which could be in production). Secondly, Scala has an outstanding IDE support, be it with IntelliJ or Visual Studio Code (with the metals plugin). These IDEs strongly increase ease of coding and productivity, as we get invaluable help, both when writing code, but also when browsing existing code and libraries. Its concise syntax allows you to express a lot in less code. Finally, Scala has access to incredibly powerful libraries such as Spark and the akka suite.

And finally, what about these complicated concepts mentioned at the beginning of this post? Just ignore them. The day you will have gone deep enough in the language to inevitably need to learn them, trust me, you’ll be ready to understand them.

In the meantime, you’ll see that Scala is an amazing language that makes your life easy.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade