5 things that developers do all the time, in Scala

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

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

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

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

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 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

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.

Mathematician, Scala enthusiast, father of two.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store