Scala 3 — Book

Scala for JavaScript Developers

Language

This page provides a comparison between the JavaScript and Scala programming languages. It’s intended for programmers who know JavaScript and want to learn about Scala, specifically by seeing examples of how JavaScript language features compare to Scala.

Overview

This section provides a relatively brief introduction and summary of the sections that follow. It presents the similarities and differences between JavaScript and Scala at a high level, and then introduces the differences you’ll experience every day as you write code.

High-level similarities

At a high level, Scala shares these similarities with JavaScript:

  • Both are considered high-level programming languages, where you don’t have to concern yourself with low-level concepts like pointers and manual memory management
  • Both have a relatively simple, concise syntax
  • Both support a C/C++/Java style curly-brace syntax for writing methods and other block of code
  • Both include features (like classes) for object-oriented programming (OOP)
  • Both include features (like lambdas) for functional programming (FP)
  • JavaScript runs in the browser and other environments like Node.js. The Scala.js flavor of Scala targets JavaScript and Scala programs can thus run in the same environments.
  • Developers write server-side applications in JavaScript and Scala using Node.js; projects like the Play Framework also let you write server-side applications in Scala
  • Both languages have similar if statements, while loops, and for loops
  • Starting at this Scala.js page, you’ll find dozens of libraries to support React, Angular, jQuery, and many other JavaScript and Scala libraries
  • JavaScript objects are mutable; Scala objects can be mutable when writing in an imperative style
  • Both JavaScript and Scala support promises as a way of handling the result of asynchronous computations (Scala concurrency uses futures and promises)

High-level differences

Also at a high level, some of the differences between JavaScript and Scala are:

  • JavaScript is dynamically typed, and Scala is statically typed
    • Although Scala is statically typed, features like type inference make it feel like a dynamic language (as you’ll see in the examples that follow)
  • Scala idioms favor immutability by default: you’re encouraged to use immutable variables and immutable collections
  • Scala has a concise but readable syntax; we call it expressive
  • Scala is a pure OOP language, so every object is an instance of a class, and symbols like + and += that look like operators are really methods; this means that you can create your own methods that work as operators
  • As a pure OOP language and a pure FP language, Scala encourages a fusion of OOP and FP, with functions for the logic and immutable objects for modularity
  • Scala has state of the art, third-party, open source functional programming libraries
  • Everything in Scala is an expression: constructs like if statements, for loops, match expressions, and even try/catch expressions all have return values
  • The Scala Native project lets you write “systems” level code, and also compiles to native executables

Programming level differences

At a lower level, these are some of the differences you’ll see every day when writing code:

  • Scala variables and parameters are defined with val (immutable, like a JavaScript const) or var (mutable, like a JavaScript var or let)
  • Scala does not use semi-colons at the end of lines
  • Scala is statically-typed, though in many situations you don’t need to declare the type
  • Scala uses traits as interfaces and to create mixins
  • In addition to simple for loops, Scala has powerful for comprehensions that yield results based on your algorithms
  • Pattern matching and match expressions will change the way you write code
  • Scala’s contextual abstractions and term inference provide a collection of features:
    • Extension methods let you add new functionality to closed classes without breaking modularity, by being available only in specific scopes (as opposed to monkey-patching, which can pollute other areas of the code)
    • Given instances let you define terms that the compiler can use to synthesize code for you
    • Type safety and multiversal equality let you limit equality comparisons—at compile time—to only those comparisons that make sense
  • Thanks to features like by-name parameters, infix notation, optional parentheses, extension methods, and higher-order functions, you can create your own “control structures” and DSLs
  • Many other goodies that you can read about throughout this book: case classes, companion classes and objects, macros, union and intersection types, multiple parameter lists, named arguments, and more

Variables and Types

Comments

//
/* ... */
/** ... */
//
/* ... */
/** ... */

Mutable variables

let   // now preferred for mutable
var   // old mutable style
var  // used for mutable variables

Immutable values

const
val

The rule of thumb in Scala is to declare variables using val, unless there’s a specific reason you need a mutable variable.

Naming standards

JavaScript and Scala generally use the same CamelCase naming standards. Variables are named like myVariableName, methods are named like lastIndexOf, and classes and object are named like Animal and PrintedBook.

Strings

Many uses of strings are similar in JavaScript and Scala, though Scala only uses double-quotes for simple strings, and triple-quotes for multiline strings.

String basics

// use single- or double-quotes
let msg = 'Hello, world';
let msg = "Hello, world";
// use only double-quotes
val msg = "Hello, world"

Interpolation

let name = 'Joe';

// JavaScript uses backticks
let msg = `Hello, ${name}`;
val name = "Joe"
val age = 42
val weight = 180.5

// use `s` before a string for simple interpolation
println(s"Hi, $name")   // "Hi, Joe"
println(s"${1 + 1}")    // "2"

// `f` before a string allows printf-style formatting.
// this example prints:
// "Joe is 42 years old, and weighs"
// "180.5 pounds."
println(f"$name is $age years old, and weighs $weight%.1f pounds.")

Multiline strings with interpolation

let name = "joe";
let str = `
Hello, ${name}.
This is a multiline string.
`;
val name = "Martin Odersky"

val quote = s"""
|$name says
|Scala is a fusion of
|OOP and FP.
""".stripMargin.replaceAll("\n", " ").trim

// result:
// "Martin Odersky says Scala is a fusion of OOP and FP."

JavaScript and Scala also have similar methods to work with strings, including charAt, concat, indexOf, and many more. Escape characters like \n, \f, \t are also the same in both languages.

Numbers and arithmetic

Numeric operators are similar between JavaScript and Scala. The biggest difference is that Scala doesn’t offer ++ and -- operators.

Numeric operators:

let x = 1;
let y = 2.0;
 
let a = 1 + 1;
let b = 2 - 1;
let c = 2 * 2;
let d = 4 / 2;
let e = 5 % 2;
val x = 1
val y = 2.0
 
val a = 1 + 1
val b = 2 - 1
val c = 2 * 2
val d = 4 / 2
val e = 5 % 2

Increment and decrement:

i++;
i += 1;

i--;
i -= 1;
i += 1;
i -= 1;

Perhaps the biggest difference is that “operators” like + and - are really methods in Scala, not operators. Scala numbers also have these related methods:

var a = 2
a *= 2      // 4
a /= 2      // 2

Scala’s Double type most closely corresponds to JavaScript’s default number type, Int represents signed 32-bit integer values, and BigInt corresponds to JavaScript’s bigint.

These are Scala Int and Double values. Notice that the type doesn’t have to be explicitly declared:

val i = 1     // Int
val d = 1.1   // Double

You can also use other numeric types as needed:

val a: Byte = 0    // Byte = 0
val a: Double = 0  // Double = 0.0
val a: Float = 0   // Float = 0.0
val a: Int = 0     // Int = 0
val a: Long = 0    // Long = 0
val a: Short = 0   // Short = 0

val x = BigInt(1_234_456_789)
val y = BigDecimal(1_234_456.890)

Boolean values

Both languages use true and false for boolean values:

let a = true;
let b = false;
val a = true
val b = false

Dates

Dates are another commonly used type in both languages.

Get the current date:

let d = new Date();

// result:
// Sun Nov 29 2020 18:47:57 GMT-0700 (Mountain Standard Time)
// different ways to get the current date and time
import java.time.*

val a = LocalDate.now
    // 2020-11-29
val b = LocalTime.now
    // 18:46:38.563737
val c = LocalDateTime.now
    // 2020-11-29T18:46:38.563750
val d = Instant.now
    // 2020-11-30T01:46:38.563759Z

Specify a different date:

let d = Date(2020, 1, 21, 1, 0, 0, 0);
let d = Date(2020, 1, 21, 1, 0, 0);
let d = Date(2020, 1, 21, 1, 0);
let d = Date(2020, 1, 21, 1);
let d = Date(2020, 1, 21);
val d = LocalDate.of(2020, 1, 21)
val d = LocalDate.of(2020, Month.JANUARY, 21)
val d = LocalDate.of(2020, 1, 1).plusDays(20)

In this case, Scala uses the date and time classes that come with Java. Many date/time methods are similar between JavaScript and Scala. See the java.time package for more details.

Functions

In both JavaScript and Scala, functions are objects, so their functionality is similar, but their syntax and terminology is a little different.

Named functions, one line:

function add(a, b) {
  return a + b;
}
add(2, 2);   // 4
// technically this is a method, not a function
def add(a: Int, b: Int) = a + b
add(2, 2)   // 4

Named functions, multiline:

function addAndDouble(a, b) {
  // imagine this requires
  // multiple lines
  return (a + b) * 2
}
def addAndDouble(a: Int, b: Int): Int =
  // imagine this requires
  // multiple lines
  (a + b) * 2

In Scala, showing the Int return type is optional. It’s not shown in the add example and is shown in the addAndDouble example, so you can see both approaches.

Anonymous functions

Both JavaScript and Scala let you define anonymous functions, which you can pass into other functions and methods.

Arrow and anonymous functions

// arrow function
let log = (s) => console.log(s)

// anonymous function
let log = function(s) {
  console.log(s);
}

// use either of those functions here
function printA(a, log) {
  log(a);
}
// a function (an anonymous function assigned to a variable)
val log = (s: String) => console.log(s)

// a scala method. methods tend to be used much more often,
// probably because they’re easier to read.
def log(a: Any) = console.log(a)

// a function or a method can be passed into another
// function or method
def printA(a: Any, f: log: Any => Unit) = log(a)

In Scala you rarely define a function using the first syntax shown. Instead, you often define anonymous functions right at the point of use. Many collections methods are higher-order functions and accept function parameters, so you write code like this:

// map method, long form
List(1,2,3).map(i => i * 10)   // List(10,20,30)

// map, short form (which is more commonly used)
List(1,2,3).map(_ * 10)        // List(10,20,30)

// filter, short form
List(1,2,3).filter(_ < 3)      // List(1,2)

// filter and then map
List(1,2,3,4,5).filter(_ < 3).map(_ * 10)   // List(10, 20)

Classes

Scala has both classes and case classes. A class is similar to a JavaScript class, and is generally intended for use in OOP style applications (though they can also be used in FP code), and case classes have additional features that make them very useful in FP style applications.

The following example shows how to create several types as enumerations, and then defines an OOP-style Pizza class. At the end, a Pizza instance is created and used:

// create some enumerations that the Pizza class will use
enum CrustSize:
  case Small, Medium, Large

enum CrustType:
  case Thin, Thick, Regular

enum Topping:
  case Cheese, Pepperoni, BlackOlives, GreenOlives, Onions

// import those enumerations and the ArrayBuffer,
// so the Pizza class can use them
import CrustSize.*
import CrustType.*
import Topping.*
import scala.collection.mutable.ArrayBuffer

// define an OOP style Pizza class
class Pizza(
  var crustSize: CrustSize,
  var crustType: CrustType
):

  private val toppings = ArrayBuffer[Topping]()

  def addTopping(t: Topping): Unit =
    toppings += t

  def removeTopping(t: Topping): Unit =
    toppings -= t

  def removeAllToppings(): Unit =
    toppings.clear()

  override def toString(): String =
    s"""
      |Pizza:
      |  Crust Size: ${crustSize}
      |  Crust Type: ${crustType}
      |  Toppings:   ${toppings}
    """.stripMargin

end Pizza

// create a Pizza instance
val p = Pizza(Small, Thin)

// change the crust
p.crustSize = Large
p.crustType = Thick

// add and remove toppings
p.addTopping(Cheese)
p.addTopping(Pepperoni)
p.addTopping(BlackOlives)
p.removeTopping(Pepperoni)

// print the pizza, which uses its `toString` method
println(p)

Interfaces, traits, and inheritance

Scala uses traits as interfaces, and also to create mixins. Traits can have both abstract and concrete members, including methods and fields.

This example shows how to define two traits, create a class that extends and implements those traits, and then create and use an instance of that class:

trait HasLegs:
  def numLegs: Int
  def walk(): Unit
  def stop() = println("Stopped walking")

trait HasTail:
  def wagTail(): Unit
  def stopTail(): Unit

class Dog(var name: String) extends HasLegs, HasTail:
  val numLegs = 4
  def walk() = println("I’m walking")
  def wagTail() = println("⎞⎜⎛  ⎞⎜⎛")
  def stopTail() = println("Tail is stopped")
  override def toString = s"$name is a Dog"

// create a Dog instance
val d = Dog("Rover")

// use the class’s attributes and behaviors
println(d.numLegs)   // 4
d.wagTail()          // "⎞⎜⎛  ⎞⎜⎛"
d.walk()             // "I’m walking"

Control Structures

Except for the use of === and !== in JavaScript, comparison and logical operators are almost identical in JavaScript and Scala.

Comparison operators

JavaScript Scala
== ==
=== ==
!= !=
!== !=
> >
< <
>= >=
<= <=

Logical operators

JavaScript Scala
&&
||
!
&&
||
!

if/then/else expressions

JavaScript and Scala if/then/else statements are similar. In Scala 2 they were almost identical, but with Scala 3, curly braces are no longer necessary (though they can still be used).

if statement, one line:

if (x == 1) { console.log(1); }
if x == 1 then println(x)

if statement, multiline:

if (x == 1) {
  console.log("x is 1, as you can see:")
  console.log(x)
}
if x == 1 then
  println("x is 1, as you can see:")
  println(x)

if, else if, else:

if (x < 0) {
  console.log("negative")
} else if (x == 0) {
  console.log("zero")
} else {
  console.log("positive")
}
if x < 0 then
  println("negative")
else if x == 0
  println("zero")
else
  println("positive")

Returning a value from if:

JavaScript uses a ternary operator, and Scala uses its if expression as usual:

let minVal = a < b ? a : b;
val minValue = if a < b then a else b

if as the body of a method:

Scala methods tend to be very short, and you can easily use if as the body of a method:

function min(a, b) {
  return (a < b) ? a : b;
}
def min(a: Int, b: Int): Int =
  if a < b then a else b

In Scala 3 you can still use the “curly brace” style, if you prefer. For instance, you can write an if/else-if/else expression like this:

if (i == 0) {
  println(0)
} else if (i == 1) {
  println(1)
} else {
  println("other")
}

Loops

Both JavaScript and Scala have while loops and for loops. Scala used to have do/while loops, but they have been removed from the language.

while loop:

let i = 0;
while (i < 3) {
  console.log(i);
  i++;
}
var i = 0;
while i < 3 do
  println(i)
  i += 1

The Scala code can also be written like this, if you prefer:

var i = 0
while (i < 3) {
  println(i)
  i += 1
}

The following examples show for loops in JavaScript and Scala. They assume that you have these collections to work with:

// JavaScript
let nums = [1, 2, 3];

// Scala
val nums = List(1, 2, 3)

for loop, single line

// newer syntax
for (let i of nums) {
  console.log(i);
}

// older
for (i=0; i<nums.length; i++) {
  console.log(nums[i]);
}
// preferred
for i <- nums do println(i)

// also available
for (i <- nums) println(i)

for loop, multiple lines in the body

// preferred
for (let i of nums) {
  let j = i * 2;
  console.log(j);
}

// also available
for (i=0; i<nums.length; i++) {
  let j = nums[i] * 2;
  console.log(j);
}
// preferred
for i <- nums do
  val j = i * 2
  println(j)

// also available
for (i <- nums) {
  val j = i * 2
  println(j)
}

Multiple generators in a for loop

let str = "ab";
for (let i = 1; i < 3; i++) {
  for (var j = 0; j < str.length; j++) {
    for (let k = 1; k < 11; k += 5) {
      let c = str.charAt(j);
      console.log(`i: ${i} j: ${c} k: ${k}`);
    }
  }
}
for
  i <- 1 to 2
  j <- 'a' to 'b'
  k <- 1 to 10 by 5
do
  println(s"i: $i, j: $j, k: $k")

Generator with guards

A guard is a name for an if expression inside a for expression.

for (let i = 0; i < 10; i++) {
  if (i % 2 == 0 && i < 5) {
    console.log(i);
  }
}
for
  i <- 1 to 10
  if i % 2 == 0
  if i < 5
do
  println(i)

for comprehension

A for comprehension is a for loop that uses yield to return (yield) a value. They’re used often in Scala.

N/A
val list =
  for
    i <- 1 to 3
  yield
    i * 10
// result: Vector(10, 20, 30)

switch & match

Where JavaScript has switch statements, Scala has match expressions. Like everything else in Scala, these truly are expressions, meaning they return a result:

val day = 1

// later in the code ...
val monthAsString = day match
  case 1 => "January"
  case 2 => "February"
  case _ => "Other"

match expressions can handle multiple matches in each case statement:

val numAsString = i match
  case 1 | 3 | 5 | 7 | 9 => "odd"
  case 2 | 4 | 6 | 8 | 10 => "even"
  case _ => "too big"

They can also be used as the body of a method:

def isTruthy(a: Matchable) = a match
  case 0 | "" => false
  case _ => true

def isPerson(x: Matchable): Boolean = x match
  case p: Person => true
  case _ => false

match expressions have many other pattern-matching options.

Collections classes

Scala has different collections classes for different needs.

Common immutable sequences are:

  • List
  • Vector

Common mutable sequences are:

  • Array
  • ArrayBuffer

Scala also has mutable and immutable Maps and Sets.

This is how you create the common Scala collection types:

val strings = List("a", "b", "c")
val strings = Vector("a", "b", "c")
val strings = ArrayBuffer("a", "b", "c")

val set = Set("a", "b", "a") // result: Set("a", "b")
val map = Map(
  "a" -> 1,
  "b" -> 2,
  "c" -> 3
)

Methods on collections

The following examples show many different ways to work with Scala collections.

Populating lists:

// to, until
(1 to 5).toList                   // List(1, 2, 3, 4, 5)
(1 until 5).toList                // List(1, 2, 3, 4)

(1 to 10 by 2).toList             // List(1, 3, 5, 7, 9)
(1 until 10 by 2).toList          // List(1, 3, 5, 7, 9)
(1 to 10).by(2).toList            // List(1, 3, 5, 7, 9)

('d' to 'h').toList               // List(d, e, f, g, h)
('d' until 'h').toList            // List(d, e, f, g)
('a' to 'f').by(2).toList         // List(a, c, e)

// range method
List.range(1, 3)                  // List(1, 2)
List.range(1, 6, 2)               // List(1, 3, 5)

List.fill(3)("foo")               // List(foo, foo, foo)
List.tabulate(3)(n => n * n)      // List(0, 1, 4)
List.tabulate(4)(n => n * n)      // List(0, 1, 4, 9)

Functional methods on sequences:

// these examples use a List, but they’re the same with Vector
val a = List(10, 20, 30, 40, 10)      // List(10, 20, 30, 40, 10)
a.contains(20)                        // true
a.distinct                            // List(10, 20, 30, 40)
a.drop(2)                             // List(30, 40, 10)
a.dropRight(2)                        // List(10, 20, 30)
a.dropWhile(_ < 25)                   // List(30, 40, 10)
a.filter(_ < 25)                      // List(10, 20, 10)
a.filter(_ > 100)                     // List()
a.find(_ > 20)                        // Some(30)
a.head                                // 10
a.headOption                          // Some(10)
a.init                                // List(10, 20, 30, 40)
a.last                                // 10
a.lastOption                          // Some(10)
a.slice(2,4)                          // List(30, 40)
a.tail                                // List(20, 30, 40, 10)
a.take(3)                             // List(10, 20, 30)
a.takeRight(2)                        // List(40, 10)
a.takeWhile(_ < 30)                   // List(10, 20)

// map, flatMap
val fruits = List("apple", "pear")
fruits.map(_.toUpperCase)             // List(APPLE, PEAR)
fruits.flatMap(_.toUpperCase)         // List(A, P, P, L, E, P, E, A, R)

val nums = List(10, 5, 8, 1, 7)
nums.sorted                           // List(1, 5, 7, 8, 10)
nums.sortWith(_ < _)                  // List(1, 5, 7, 8, 10)
nums.sortWith(_ > _)                  // List(10, 8, 7, 5, 1)

List(1,2,3).updated(0,10)             // List(10, 2, 3)
List(2,4).union(List(1,3))            // List(2, 4, 1, 3)

// zip
val women = List("Wilma", "Betty")    // List(Wilma, Betty)
val men = List("Fred", "Barney")      // List(Fred, Barney)
val couples = women.zip(men)          // List((Wilma,Fred), (Betty,Barney))

Scala has many more methods that are available to you. The benefits of all these methods are:

  • You don’t have to write custom for loops to solve problems
  • When you read someone else’s code, you won’t have to read their custom for loops; you’ll just find common methods like these, so it’s easier to read code from different projects

Tuples

When you want to put multiple data types in the same list, JavaScript lets you do this:

let stuff = ["Joe", 42, 1.0];

In Scala you do this:

val a = ("eleven")
val b = ("eleven", 11)
val c = ("eleven", 11, 11.0)
val d = ("eleven", 11, 11.0, Person("Eleven"))

In Scala these types are called tuples, and as shown, they can contain one or more elements, and the elements can have different types. You access their elements just like you access elements of a List, Vector, or Array:

d(0)   // "eleven"
d(1)   // 11

Enumerations

JavaScript doesn’t have enumerations, but you can do this:

let Color = {
  RED: 1,
  GREEN: 2,
  BLUE: 3
};
Object.freeze(Color);

In Scala 3 you can do quite a few things with enumerations. You can create an equivalent of that code:

enum Color:
  case Red, Green, Blue

You can create a parameterized enum:

enum Color(val rgb: Int):
  case Red   extends Color(0xFF0000)
  case Green extends Color(0x00FF00)
  case Blue  extends Color(0x0000FF)

You can also create user-defined enum members:

enum Planet(mass: Double, radius: Double):
  case Mercury extends Planet(3.303e+23, 2.4397e6)
  case Venus   extends Planet(4.869e+24,6.0518e6)
  case Earth   extends Planet(5.976e+24,6.37814e6)
  // more planets here ...

  private final val G = 6.67300E-11
  def surfaceGravity = G * mass / (radius * radius)
  def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity

Scala.js DOM Code

Scala.js lets you write Scala code that is compiled to JavaScript code that can then be used in the browser. The approach is similar to TypeScript, ReScript, and other languages that are compiled to JavaScript.

Once you include the necessary libraries, and import the necessary packages in your project, writing Scala.js code looks very similar to writing JavaScript code:

// show an alert dialog on a button click
jQuery("#hello-button").click{() =>
  dom.window.alert("Hello, world")
}

// define a button and what should happen when it’s clicked
val btn = button(
  "Click me",
  onclick := { () =>
    dom.window.alert("Hello, world")
  })

// create two divs with css classes, an h2 element, and the button
val content =
  div(cls := "foo",
    div(cls := "bar",
      h2("Hello"),
      btn
    )
  )

// add the content to the DOM
val root = dom.document.getElementById("root")
root.innerHTML = ""
root.appendChild(content.render)

Note that although Scala is a type-safe language, no types are declared in the above code. Scala’s strong type inference capabilities often make Scala code look like it’s dynamically typed. But it is type-safe, so you catch many classes of errors early in the development cycle.

Other Scala.js resources

The Scala.js website has an excellent collection of tutorials for JavaScript developers interested in using Scala.js. Here are some of their initial tutorials:

Concepts that are unique to Scala

There are other concepts in Scala which currently have no equivalent in JavaScript:

  • Almost everything related to contextual abstractions
  • Method features:
    • Multiple parameter lists
    • Using named arguments when calling methods
  • Using traits as interfaces
  • Case classes
  • Companion classes and objects
  • The ability to create your own control structures and DSLs
  • Advanced features of match expressions and pattern matching
  • for comprehensions
  • Infix methods
  • Macros and metaprogramming
  • More …

Contributors to this page: