Beginner

Operators in Scala

Learn arithmetic, comparison, logical, and bitwise operators in Scala, plus how every operator is really a method call

Operators are the verbs of a programming language—they combine values into expressions that compute results. Scala supports the familiar arithmetic, comparison, logical, and bitwise operators you would expect from any C-family language, but it approaches them from a strikingly different angle.

In Scala, every value is an object and every operation is a method call. There is no special category of “primitive operators” baked into the compiler the way there is in C or Java. When you write 2 + 3, you are calling the + method on the integer 2 with the argument 3. This unification of operators and methods is one of the clearest expressions of Scala’s philosophy: object-oriented all the way down, yet expressive enough to read like ordinary math.

This page covers Scala’s arithmetic, relational, logical, and bitwise operators, how operator precedence works, and how you can define your own operators by writing methods. Because Scala is statically and strongly typed with inference, the type of every expression is determined at compile time—so a few rules about integer versus floating-point division matter in practice.

Arithmetic Operators

The standard arithmetic operators work on numeric types. The key gotcha for newcomers: dividing two integers performs integer division (the fractional part is discarded). To get a fractional result you need at least one floating-point operand.

Create a file named Arithmetic.scala:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
object Arithmetic {
  def main(args: Array[String]): Unit = {
    val a = 17
    val b = 5

    println(s"a + b  = ${a + b}")          // addition
    println(s"a - b  = ${a - b}")          // subtraction
    println(s"a * b  = ${a * b}")          // multiplication
    println(s"a / b  = ${a / b}")          // integer division
    println(s"a % b  = ${a % b}")          // remainder (modulo)

    // Floating-point division keeps the fractional part
    val x = 17.0
    val y = 5.0
    println(s"x / y  = ${x / y}")

    // Every operator is really a method call: a + b is a.+(b)
    println(s"a.+(b) = ${a.+(b)}")

    // Scala has no ++ or -- operators; use compound assignment instead
    var counter = 0
    counter += 1
    counter += 1
    println(s"counter = $counter")
  }
}

Notice the use of string interpolation with s"...${expression}..."—an s-prefixed string lets you embed expressions directly. Notice too that there are no ++ or -- operators in Scala; you increment a var with counter += 1. The += form works because var bindings are mutable, while val bindings are immutable.

Comparison and Logical Operators

Relational operators compare values and produce a Boolean. Logical operators combine Boolean values, and && and || short-circuit—the right-hand side is only evaluated if it is needed.

A subtle but important difference from Java: in Scala, == compares values (it delegates to the .equals method), so comparing two strings with == checks their contents, not their object identity.

Create a file named Comparison.scala:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
object Comparison {
  def main(args: Array[String]): Unit = {
    val a = 17
    val b = 5

    // Relational operators return Boolean
    println(s"a > b   : ${a > b}")
    println(s"a < b   : ${a < b}")
    println(s"a >= b  : ${a >= b}")
    println(s"a == b  : ${a == b}")
    println(s"a != b  : ${a != b}")

    // == compares values (structural equality), unlike Java's reference check
    val s1 = "scala"
    val s2 = "sca" + "la"
    println(s"s1 == s2: ${s1 == s2}")

    // Logical operators with short-circuit evaluation
    val sunny = true
    val warm = false
    println(s"sunny && warm: ${sunny && warm}")
    println(s"sunny || warm: ${sunny || warm}")
    println(s"!sunny       : ${!sunny}")
  }
}

Precedence, Bitwise, and Custom Operators

When operators appear without parentheses, Scala resolves them by precedence: * binds more tightly than +, just as in arithmetic. Scala determines precedence from the first character of the operator, so you rarely need to memorize a long table—the rules match mathematical intuition.

This example also shows bitwise operators and Scala’s most distinctive feature: because operators are methods, any method that takes a single argument can be used in infix position, and you can define your own operators simply by naming a method +, *, or anything else.

Create a file named Operators.scala:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
object Operators {
  def main(args: Array[String]): Unit = {
    // Precedence: * binds tighter than +
    println(s"2 + 3 * 4   = ${2 + 3 * 4}")
    println(s"(2 + 3) * 4 = ${(2 + 3) * 4}")

    // Bitwise operators on integers
    println(s"6 & 3  = ${6 & 3}")    // bitwise AND
    println(s"6 | 3  = ${6 | 3}")    // bitwise OR
    println(s"6 ^ 3  = ${6 ^ 3}")    // bitwise XOR
    println(s"1 << 4 = ${1 << 4}")   // left shift

    // Any single-argument method can be used as an infix operator
    val list = List(2, 3) ++ List(4, 5)
    println(s"list = $list")

    // Define your own operator by writing a method named +
    case class Vec(x: Int, y: Int) {
      def +(other: Vec): Vec = Vec(x + other.x, y + other.y)
    }
    val sum = Vec(1, 2) + Vec(3, 4)
    println(s"sum = $sum")
  }
}

The Vec example captures the heart of Scala’s design. The + we defined is an ordinary method, yet it reads exactly like built-in addition. This is how libraries such as Spark and Cats provide rich, math-like APIs—they are just methods with symbolic names.

Running with Docker

Each example is a self-contained Scala source file. Run them with Scala CLI, which compiles and executes in one step.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Pull the official image
docker pull virtuslab/scala-cli:latest

# Run the arithmetic example
docker run --rm -v $(pwd):/app -w /app virtuslab/scala-cli:latest run Arithmetic.scala

# Run the comparison and logical example
docker run --rm -v $(pwd):/app -w /app virtuslab/scala-cli:latest run Comparison.scala

# Run the precedence and custom operator example
docker run --rm -v $(pwd):/app -w /app virtuslab/scala-cli:latest run Operators.scala

Expected Output

Running Arithmetic.scala:

a + b  = 22
a - b  = 12
a * b  = 85
a / b  = 3
a % b  = 2
x / y  = 3.4
a.+(b) = 22
counter = 2

Running Comparison.scala:

a > b   : true
a < b   : false
a >= b  : true
a == b  : false
a != b  : true
s1 == s2: true
sunny && warm: false
sunny || warm: true
!sunny       : false

Running Operators.scala:

2 + 3 * 4   = 14
(2 + 3) * 4 = 20
6 & 3  = 2
6 | 3  = 7
6 ^ 3  = 5
1 << 4 = 16
list = List(2, 3, 4, 5)
sum = Vec(4, 6)

Key Concepts

  • Operators are method callsa + b is shorthand for a.+(b). There are no special primitive operators; everything is a method on an object.
  • Integer vs floating-point division17 / 5 yields 3 (integer division), while 17.0 / 5.0 yields 3.4. Mix in at least one Double to keep the fraction.
  • No ++ or -- — Scala deliberately omits increment/decrement operators. Use compound assignment like counter += 1 on a mutable var.
  • == is structural — Unlike Java, == compares values by calling .equals, so string and object comparisons check content, not reference identity.
  • Short-circuit logic&& and || only evaluate their right operand when necessary, which is useful for guarding against null or expensive checks.
  • Precedence from the first character — Scala derives operator precedence from an operator’s leading character, matching standard mathematical expectations (* before +).
  • Define your own operators — Any single-argument method can be used infix, and you can name a method with symbols (+, *, <*>) to build expressive, math-like APIs.

Running Today

All examples can be run using Docker:

docker pull virtuslab/scala-cli:latest
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining