About this ‘A Language a Day’ Advent Calendar
This article became a part of my book ‘A Language a Day’, which you can get in both electronic and paper format. Languages covered in the book: C++, Clojure, Crystal, D, Dart, Elixir, Factor, Go, Hack, Hy, Io, Julia, Kotlin, Lua, Mercury, Nim, OCaml, Raku, Rust, Scala, and TypeScript.
Welcome to Day 7 of this year’s A Language a Day Advent Calendar. Today’s topic is introduction to the Scala programming language.
Facts about the language
Some facts about Scala:
- Based on Java (both syntactically and using JVM)
- Supports object-oriented and functional styles
- Statically typed
- Appeared in 2004
- Website: www.scala-lang.org
Installing and running Scala
To run programs in Scala, you need first to have Java installed. Then, you can install either IntelliJ or a command-line set, sbt
. Here’s the command line for the Mac users:
$ brew install sbt
On the main website, you can find a binary for your operating system.
To run a program, compile it to the JVM bytecode and then run it:
$ scalac hello-world.scala
$ scala HelloWorld
For the rest of this article, you can simplify the process and execute the code with a single command:
$ scala hello-world.scala
Hello, World!
Surprisingly, it is wise to take a look at three versions of the Hello, World! program. This will immediately give you some additional view of Scala’s relation to Java.
The simplest program is a one-liner:
println("Hello, World!")
This resembles a program in a scripting language. You can use the Java approach instead:
object HelloWorld { def main(args: Array[String]) = { println("Hello, World!") } }
Here, the main
function is defined in the Hello object (not class).
Finally, the Scala-way variant:
object HelloWorld extends App { println("Hello, World!") }
The object
keyword makes a single instance of such object, but this time it extends App
. App
is a pre-defined trait that has its own main
method, so the program runs the code to print our message when the object is instantiated.
Variables
To create a variable, you use one of the two keywords: var
or val
. The var
keyword creates a mutable variable, the val
— immutable. The val
variables in Scala are equivalent to the final
variables in Java and they cannot be re-assigned.
In the following program, the greeting is made up from three parts, one of them is a variable containing the name:
val name = "John" println("Hello, " + name + "!")
In this case, the type of the variable is known at compile time, but you can specify it explicitly.
val name: String = "John" println("Hello, " + name + "!")
This is an error to leave a new variable undefined. The following code will not compile:
var name: String // error: '=' expected name = "John"
Variables can be interpolated in strings:
val name = "John" var age = 32 println(s"$name is $age years old.")
It is possible to interpolate a simple expression as well:
println(s"Next year he'll be ${age + 1}.")
Functions
Functions are declared with the def
keyword. You have to specify the type of its parameters and optionally the result type. You supply the body of the function after the =
sign. In the simple cases, no curly braces are needed:
def add(a: Int, b: Int): Int = a + b println(add(42, 43))
The same function can be re-written with a traditional code block (but still with =
):
def add(a: Int, b: Int): Int = { a + b // or: return a + b }
A Factorial example
Let us implement computation of a factorial.
def factorial(n: Int): Int = if (n < 2) 1 else n * factorial(n - 1) println(factorial(1)) // 1 println(factorial(4)) // 24 println(factorial(5)) // 120
Here, the whole body is a one-liner with the ternary if
. Notice that the factorial
function does not change any variables, and is thus an implementation in the spirit of functional programming ideology.
An alternative way to implement it is to use a range.
def factorial(n: Int): Int = (1 to n).product
Later on this page, there will be an example of lambda functions (see the Sleep sort section).
Classes
Classes are defined with the class
keyword.
In the simplest cases, when you need a class with data fields only, you only need to declare a constructor with the var
(or val
) parameters, which you can directly use as object properties. Objects are instantiated with the new
keyword.
class Person(val name: String, var age: Int) val p = new Person("Karla", 20) println(p.name) // Karla p.age += 1 println(p.age) // 21
Class methods
Class methods are functions defined in the class body. They have direct access to the fields of the instances. You do not need to pass or to use any form of the this
pointer.
class Person(var name: String, val age: Int) { def info(): String = s"${name} is ${age} years old." } var p = new Person("Mike", 25) println(p.info())
Singleton objects
In Scala, you can’t have static class methods, but you can instead create a singleton object. All you need to do is to use the object
keyword in place of object
. Actually, you don’t have to instantiate it ever more.
object Counter { var count: Int = 0 def get_count(): Int = { count += 1 count } } println(Counter.get_count) // 1 println(Counter.get_count) // 2 println(Counter.get_count) // 3
In this example, the count
variable is a single field belonging to a single instance of the Counter
object.
A polymorphic example
Scala allows us to use traits and abstract classes. Let us create an array of polymorphic objects and loop over them to see if the correct overloaded methods were called.
trait Animal { def info(): String } class Dog extends Animal { def info() = "Dog" } class Cat extends Animal { def info() = "Cat" } var zoo = Array(new Cat, new Dog, new Cat, new Dog) for (x <- zoo) println(x.info)
Instead of a trait, we could use an abstract class:
abstract class Animal { def info(): String }
The type restriction in the trait (or the abstract class) forces all info
methods to return a string.
Concurrency
There are a few means that you can choose for concurrent programming. Among them, are threads and futures.
Sleep sort
Let us use futures to implement the sleep sort algorithm. The below implementation is intendedly written in the functional style.
import scala.concurrent.{Future, Await} import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration.Duration val data = Array(10, 4, 2, 6, 2, 7, 1, 3) data.map( (n) => { Future { Thread.sleep(n * 100) println(n) } } ).map( (f) => Await.result(f, Duration.Inf) )
Futures require some additional imports and the execution context. Note how you can use curly braces to import two modules whose paths differ partially only.
For each data element, the map
method creates an instance of a Future
that sleeps the given number of milliseconds and prints the number. Inside map
, we have an anonymous function (n) => {...}
, or (n: Int) => {...}
alternatively.
On top of the first mapping, another one is called. It receives the futures (one per data item) and calls another lambda to make sure the program waits for the completion of the future.
If you do not like this approach, you can create a separate function taking a single argument, and pass its name to map
. Here is the version of the part of the above program that does not use lambda functions embedded to the map
calls. Notice that you cannot omit type annotation of function parameters.
def f1(n: Int) = Future { Thread.sleep(n * 100) println(n) } def f2(f: Future[Unit]) = Await.result(f, Duration.Inf) data.map(f1).map(f2)
Get more
If you’d like to continue learning Scala, you have to learn a lot more things that were just covered. In particular, understanding the object hierarchy is required. For example, the difference between traits and abstract classes, multiple inheritance tricks, companion objects, etc. A separate set of features belongs to functional programming, such as filters, closures, tail recursion, etc.
You can continue with the official documentation, where you can find the full reference, a tour, the Scala book, and a lot more.
The code for today’s article you can find on GitHub.
Next: Day 8. Dart