This is a short introduction to the Scala language for those familiar with JavaScript ES6. We are comparingto ES6 instead of earlier versions of JavaScript because ES6 contains many nice features and syntax changes that bringit closer to Scala.

The best way to experiment with Scala is to use a Scala REPL, or the worksheetfunctionality in the ScalaIDE or IntelliJIDEA.

For more reading check out Scala Exercises, ScalaSchool and official Scala Tutorials.

The Scala language

Scala is a modern multi-paradigm programming language designed to express common programming patterns in a concise,elegant, and type-safe way. It smoothly integrates features of object-oriented and functional languages. Scala is apure object-oriented language in the sense that every value is an object. It is also a functional language in the sensethat every function is a value and that immutability is favored over mutability.

This combination of paradigms is not alien to JavaScript developers.JavaScript is also object-oriented, although primitive values are not considered as objects.It is at least partially functional as well, since functions are values and can be passedaround to other functions (such as Array.prototype.map).However, although it is possible to write with an immutable mindset in JavaScript,the language does not encourage immutability.

The biggest difference to JavaScript is that Scala is statically typed. This means that it is equipped with anexpressive type system that enforces statically that abstractions are used in a safe and coherent manner, meaning thecompiler will catch many typical programming errors. If you have used other statically typed languages like Java or C#before you may have noticed that type definitions are all over the place. This is not true with Scala where the compilercan infer most of the types automatically.

Variables

Let’s start with something simple, variables. Both Scala and ES6 support mutable and immutable variables.

ES6

  1. // mutable variable
  2. let x = 5;
  3. // immutable variable
  4. const y = "Constant";

Scala

  1. // mutable variable
  2. var x = 5
  3. // immutable variable
  4. val y = "Constant"

Note that the Scala compiler automatically infers the types for x and y from the values that are assigned. In Scalaboth mutable and immutable variables must always be initialized when declared.

Primitive types

Scala defines several primitive types, of which most have corresponding types in JavaScript as well. The following table presents the most commonly used ones (there are also Byte, Short and Float).

Scala typeJavaScript typeNotes
Stringstring
Booleanboolean
Intnumberinteger, range (-2147483648, 2147483647)
Doublenumber64-bit floating point, fully equivalent to JS number
LongN/A64-bit integer
CharN/AUTF-16 code unit
Unitundefined
Nullnull

In JavaScript all numbers are represented as 64-bit floating point internally, which may give surprising results whenmaking some calculations. In Scala calculations are always performed using the types of operands, so dividing an Intwith another Int, the result is rounded to an Int.

ES6

  1. const x = 5 / 3; // == 1.6666666666666667

Scala

  1. val x = 5 / 3 // == 1
  2. val y = 5.0 / 3 // == 1.6666666666666667
  3. val z = 5 / 3.0 // == 1.6666666666666667

Because in JavaScript every number is a number there is no need to do type conversions. In Scala, however, it is anerror if you try to assign a higher precision value to a lower precision variable. You must explicitly convert it usingan appropriate function.

Scala

  1. val x: Double = 3 // Ok!
  2. val y: Int = 3.5 // Compile error
  3. val z: Int = 3.5.toInt // Ok!
  4. val a: Int = x // Compile error
  5. val b: Int = x.toInt // Ok!

Functions

Defining functions is quite similar in both languages.You just replace the function keyword with def, and add types for theparameters and the result type.Speaking of which, this is about the only place you will explicitly write typesin Scala. The types of local values inside functions are usually inferred by thecompiler.Actually, even the result type will usually be inferred too, but it is goodpractice to explicitly define it, to help catch potential type errors.

Note that there is no need for a return keyword: the last expression in thefunction is automatically used as the return value.

ES6

  1. function mult(x, y) {
  2. return x * y;
  3. }

Scala

  1. def mult(x: Double, y: Double): Double = x * y

Anonymous functions

In functional programming you quite often need to provide a function as a parameter, but you don’t need it elsewhere soit can be anonymous. Both languages support the nice “fat arrow” notation for defining anonymous functions conveniently.

ES6

  1. const f = (x, y) => x + y;
  2. const p = ["Fox", "jumped", "over", "me"];
  3. const l = p.map(s => s.length)
  4. .reduce((a, b) => a + b, 0); // == 15

Scala

  1. val f = (x: Double, y: Double) => x + y
  2. val p = Array("Fox", "jumped", "over", "me")
  3. val l = p.map(s => s.length)
  4. .foldLeft(0)((a, b) => a + b) // == 15

Default, named and rest parameters

You can also define default values for parameters if they are not supplied when the function is called. For variablenumber of parameters, you can access those as a Seq (see thecollections part of this tour for more info).Named parameters work just as you would expect in Scala, whereasin ES6 you need to supply them with the object notation.

ES6

  1. // default value
  2. function mult(x, y = 42.0) {
  3. return x * y;
  4. }
  5. // variable number of parameters
  6. function sum(...args) {
  7. return args.reduce((a, b) => a + b, 0);
  8. }
  9. const s = sum(5, 4, 3, 2, 1); // == 15
  10. // named parameters
  11. function vec({x = 0, y = 0, z = 0}) {
  12. return new Vec(x, y, z);
  13. }
  14. const v = vec({x: 8, z: 42}); // Vec(8, 0, 42)

Scala

  1. // default value
  2. def mult(x: Double, y: Double = 42.0): Double =
  3. x * y
  4. // variable number of parameters
  5. def sum(args: Double*): Double =
  6. args.foldLeft(0.0)((a, b) => a + b)
  7. val s = sum(5, 4, 3, 2, 1) // == 15
  8. // named parameters (works directly)
  9. def vec(x: Int = 0, y: Int = 0, z: Int = 0): Vec =
  10. new Vec(x, y, z)
  11. val v = vec(8, z = 42) // Vec(8, 0, 42)

Again, the Scala compiler can infer all the required types in the code above, including the parameters for the anonymousfunction given to the foldLeft function.

if, while, for, match control structures

As you would expect, Scala has the regular if-else and while control structures found in most programminglanguages. The big difference to JavaScript is that if statements are actually expressions returning a value. InJavaScript you have the special a ? b : c construct to achieve the same result.

ES6

  1. const res = (name === "") ? 0 : 1;

Scala

  1. val res = if (name == "") 0 else 1

The for construct in Scala is quite different from the for-loop in JavaScript and also much more powerful. You can useit to iterate over numerical ranges or collections in both languages:

ES6

  1. let x = 0;
  2. for (let i = 0; i < 100; i++)
  3. x += i * i;
  4. const p = ["Fox", "jumped", "over", "me"];
  5. for (let s of p) {
  6. console.log(`Word ${s}`);
  7. }

Scala

  1. var x = 0
  2. for (i <- 0 until 100)
  3. x += i * i
  4. val p = Array("Fox", "jumped", "over", "me")
  5. for (s <- p) {
  6. println(s"Word $s")
  7. }

In case you have nested for-loops, you can easily combine them into one for-comprehension in Scala. Inside the foryou can even filter using if expressions. In Scala a for-comprehension is just syntactic sugar for a series offlatMap, map and withFilter calls making it very handy when dealing with Scala collections.

ES6

  1. function findPairs(n, sum) {
  2. for (let i = 0; i < n; i++) {
  3. for (let j = i; j < n; j++) {
  4. if (i + j == sum)
  5. console.log(`Found pair ${i}, ${j}`);
  6. }
  7. }
  8. };
  9. findPairs(20, 31);

Scala

  1. def findPairs(n: Int, sum: Int): Unit = {
  2. for {
  3. i <- 0 until n
  4. j <- i until n if i + j == sum
  5. } println(s"Found pair $i, $j")
  6. }
  7. findPairs(20, 31)

The code above also serves as an example for string interpolation (in Scala) and template strings (in ES6). Bothmake it easier to construct strings using variables or function calls. In Scala you don’t need to enclose the variablein {} if it’s just a simple variable name. For more complex cases you’ll need to use the s"Length = ${data.length}"syntax.

Finally the match construct provides pattern matching capabilities in Scala. Pattern matching is a complex topiccovered in more detail in the advanced section of this article, so here we just focus on the simple use cases likereplacing JavaScript switch/case with it.

ES6

  1. const animal = "Dog";
  2. let description;
  3. switch(animal) {
  4. case "Cat":
  5. case "Lion":
  6. case "Tiger":
  7. description = "It's feline!";
  8. break;
  9. case "Dog":
  10. case "Wolf":
  11. description = "It's canine!";
  12. break;
  13. default:
  14. description = "It's something else";
  15. }
  16. console.log(description);

Scala

  1. val animal = "Dog"
  2. val description = animal match {
  3. case "Cat" | "Lion" | "Tiger" =>
  4. "It's feline!"
  5. case "Dog" | "Wolf" =>
  6. "It's canine!"
  7. case _ =>
  8. "It's something else"
  9. }
  10. println(description)

In Scala you can use the |-operator to match multiple choices and there is no need (nor support) for break, as casesnever fall through like they do in JavaScript. For the default case, use the ubiquitous _ syntax (it has many manymore uses in Scala!) As with if, a match is an expression returning a value that you can directly assign to avariable.

Classes

Being an object-oriented language, Scala naturally supports classes with inheritance. In addition to basic classes Scalaalso has:

  • case classes for conveniently storing data
  • objects for singletons
  • traits for defining interfaces and mixins

Let us look at a simple class hierarchy in both languages.

ES6

  1. class Shape {
  2. constructor(x, y) {
  3. this.x = x;
  4. this.y = y;
  5. }
  6. move(dx, dy) {
  7. this.x += dx;
  8. this.y += dy;
  9. }
  10. draw() {
  11. console.log(`Shape at ${this.x}, ${this.y}`);
  12. }
  13. };
  14. class Circle extends Shape {
  15. constructor(x, y, r) {
  16. super(x, y);
  17. this.r = r;
  18. }
  19. draw() {
  20. console.log(`Circle at ${this.x}, ${this.y} with radius ${this.r}`);
  21. }
  22. }
  23. const c = new Circle(5, 5, 42);
  24. const r = c.r; // == 42

Scala

  1. // use var to make coordinates mutable
  2. abstract class Shape(var x: Int, var y: Int) {
  3. def move(dx: Int, dy: Int): Unit = {
  4. x += dx
  5. y += dy
  6. }
  7. def draw(): Unit = {
  8. println(s"Shape at $x, $y")
  9. }
  10. }
  11. // r is immutable but accessible outside the class
  12. class Circle(x: Int, y: Int, val r: Int)
  13. extends Shape(x, y) {
  14. override def draw(): Unit = {
  15. println(s"Circle at $x, $y with radius $r")
  16. }
  17. }
  18. val c = new Circle(5, 5, 42)
  19. val r = c.r // == 42

Note that this is typically omitted in Scala, since the compiler can tell thatx, y and r are properties of the enclosing class (and not local variables).

Case classes

Case classes are a particular kind of class in Scala which have a lot ofcompiler-generated goodies. They are particularly suited for immutable datacontainers whose instances are equal if and only if their fields are pairwiseequal. They also automatically receive a sensible toString() representation,and they can be instantiated without the new keyword.

JavaScript doesn’t quite have a similar construct, but whenever you would usethe regular object notation, consider using a case class instead.

ES6

  1. const person = {first: "James", last: "Bond"};

Scala

  1. case class Person(first: String, last: String)
  2. val person = Person("James", "Bond")

Case classes enforce type safety and prevent constructing invalid objects with missing fields.

The Scala compiler automatically generates a proper equals method for case classes, making comparing them trivial. In ES6you would typically go for a library like lodash to avoid writing the complexcomparison code yourself.

ES6

  1. const o1 = {a: 1, x: "test"};
  2. const o2 = {a: 1, x: "test"};
  3. if (o1 != o2) {
  4. // this doesn't work as expected
  5. console.log("They are not equal!");
  6. }
  7. if (_.isEqual(o1, o2)) {
  8. console.log("They are equal!");
  9. }

Scala

  1. case class AX(a: Int, x: String)
  2. val o1 = AX(1, "test")
  3. val o2 = AX(1, "test")
  4. if (o1 == o2) {
  5. println("They are equal!")
  6. }

Fields in case classes are public and immutable by default (unless you define them with var modifier) so you cannot make changesto instances. Instead of modifying the instance you make a copy and modify one or more fields during the copy. Scalaprovides a suitable copy function for each case class automatically. In ES6 you can use Object.assign to achieve thesame result.

ES6

  1. const o1 = {a: 1, x: "test"};
  2. // start with empty object to prevent
  3. // modification of o1
  4. const o2 = Object.assign({}, o1, {a: 42});

Scala

  1. case class AX(a: Int, x: String)
  2. val o1 = AX(1, "test")
  3. val o2 = o1.copy(a = 42)

Finally case classes can be used nicely in pattern matching, which is covered in the advanced section.

Objects

An object is a special class with only a single instance: a singleton. JavaScript also has a singleton design pattern(or actually several) even though the language itself does not have direct support for the concept. Singletons areuseful for putting stuff in a shared namespace without polluting the global scope.

ES6

  1. const RandomGen = {
  2. _privateMethod() {
  3. console.log("I am private");
  4. },
  5. _rnd() {
  6. return Math.random()
  7. },
  8. publicMethod() {
  9. console.log("The public can see me!");
  10. this._privateMethod();
  11. },
  12. name: "RandomGen",
  13. getRandomNumber() {
  14. return this._rnd()
  15. }
  16. }
  17. const r = RandomGen.getRandomNumber();

Scala

  1. import scala.util.Random
  2. object RandomGen {
  3. private def privateMethod(): Unit = {
  4. println("I am private")
  5. }
  6. private val rnd = new Random()
  7. def publicMethod(): Unit = {
  8. println("The public can see me!")
  9. privateMethod()
  10. }
  11. val name = "RandomGen"
  12. def getRandomNumber: Double = rnd.nextDouble()
  13. }
  14. val r = RandomGen.getRandomNumber

As you can see, defining singleton objects in Scala is quite trivial thanks to the native support in the language.

Another common use for objects in Scala is using them as companion objects for classes to store static variablesand methods shared by all instances of the class.

Traits

Scala traits are similar to the mixin design pattern in JavaScript by allowing developer to define behaviors for classcomposition. Because of Scala’s strict type system, traits are commonly used to describe common interfaces for a groupof implementation classes. JavaScript itself has no need for interfaces, but some extensions like TypeScript supportthem for the same purpose as Scala.

ES6

  1. class Circle extends Shape {
  2. constructor(x, y, r) {
  3. super(x, y);
  4. this.r = r;
  5. }
  6. draw() {
  7. console.log(`Circle at ${this.x}, ${this.y} with radius ${this.r}`);
  8. }
  9. }
  10. const Clickable = {
  11. onClick() {
  12. console.log("Clicked!");
  13. }
  14. };
  15. class ClickableCircle extends Circle {}
  16. Object.assign(ClickableCircle.prototype, Clickable);
  17. const cc = new ClickableCircle(0, 0, 42);
  18. cc.onClick();

Scala

  1. class Circle(x: Int, y: Int, val r: Int)
  2. extends Shape(x, y) {
  3. override def draw(): Unit = {
  4. println(s"Circle at $x, $y with radius $r")
  5. }
  6. }
  7. trait Clickable {
  8. def onClick(): Unit = {
  9. println("Clicked!")
  10. }
  11. }
  12. class ClickableCircle(x: Int, y: Int, r: Int)
  13. extends Circle(x, y, r) with Clickable
  14. val cc = new ClickableCircle(0, 0, 42)
  15. cc.onClick()

Note that there are many ways for defining mixins in JavaScript, using Object.assign is just one of them supported byES6.

Option, the type safe undefined

The notorious undefined type in JavaScript can be a blessing or a curse. On the other hand it makes life easy byallowing you to drop function parameters or leave variables undefined. But then it also masks many errors and makesyou write extra code to check for undefined. Quite often undefined is used to make a distinction between anexisting value (of any type) and a missing value.

Scala doesn’t have undefined (it does have null but its use is discouraged), but instead it has anOption trait for representing optional values. In Scala.js the undefined type exists to supportinteroperability with JS libraries, but even there it is recommended to use Option whenever possible.

Option[A] is a container for an optional value of type A(note that Option[A] is Scala’s notation for type parameters, which mostprogramming languages, including TypeScript, write as Option<A>).If the value of type A is present, the Option[A] is an instance of Some[A],containing the present value of type A. If the value is absent, the Option[A]is the object None.

ES6

  1. function log(msg, context) {
  2. let s;
  3. if (context !== undefined)
  4. s = `[${context}] ${msg}`;
  5. else
  6. s = msg;
  7. console.log(s);
  8. };
  9. // produces: First message
  10. log("First message");
  11. // produces: [debug] Second message
  12. log("Second message", "debug");

Scala

  1. def log(msg: String,
  2. context: Option[String] = None): Unit = {
  3. val s = context match {
  4. case Some(c) => s"[$c] $msg"
  5. case None => msg
  6. }
  7. println(s)
  8. }
  9. log("First message")
  10. log("Second message", Some("debug"))

Pattern matching works nicely with Option, but there are more powerful ways to use it. Let’s rewrite the previousfunction another way giving us the same result.

Scala

  1. def log(msg: String, context: Option[String] = None): Unit = {
  2. val s = context.map(c => s"[$c] $msg").getOrElse(msg)
  3. println(s)
  4. }

Whoa, quite a reduction in code size! Next let’s see how we can process a sequence of option values.

ES6

  1. const data = [1, 2, 3, undefined, 5, undefined, 7];
  2. const res = data.filter((x) => x !== undefined);

Scala

  1. val data = Array(Some(1), Some(2), Some(3),
  2. None, Some(5), None, Some(7))
  3. val res = data.filter(x => x.isDefined)

Option provides many collection like methods like map, filter and flatMap, which are discussed in the nextchapter.