Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 2

Python to Scala: Core Concepts

Recommended resources

Hello! Welcome back to your accelerated training for the Senior Dataflow & Integration Engineer role.

In our first session, you successfully set up a professional Scala development environment using Helix and Metals. You're now equipped with the essential tooling.

This lesson builds directly on that foundation. Our goal is to translate core Python concepts (variables, functions, collections) to their idiomatic Scala equivalents (val/var, def, List/Map). By the end of this session, you'll be able to read and write basic Scala code by mapping it to concepts you're already an expert in from Python and Go. This is the crucial first step into the language before we start building data pipelines.

From Python to Scala: A Quick Orientation

Before we dive into syntax, it's useful to understand the philosophical similarities and differences that will shape our journey. Python is dynamically typed and interpreted, offering great flexibility. Scala is statically typed and compiled to run on the JVM, providing compile-time safety and performance. However, as you'll see, Scala's powerful type inference often makes it feel as concise as a dynamic language.

The most significant mindset shift will be embracing immutability by default, a core tenet of functional programming that is central to building robust, parallelizable data pipelines in Beam.

Let's start by translating the most fundamental building block: variables.


1. Declaring Data: val, var, and Type Inference

In Python, you declare a variable and can freely change its type and value. Scala is more explicit, forcing a distinction between mutable and immutable variables right at declaration. This is one of the most important concepts in idiomatic Scala.

  • val (from "value"): Declares an immutable variable, similar to a constant. Once assigned, it cannot be reassigned. This is the preferred way to declare variables in Scala.
  • var (from "variable"): Declares a mutable variable. Its value can be changed after assignment. You should only use var when you have a specific reason for mutation.

The article below provides excellent side-by-side comparisons for a Python developer.

Scala for Python Developers

Please read the following sections from the 'Scala for Python Developers' documentation. It provides a direct translation of familiar Python syntax for variables and collections.

Focus on the 'Variable assignment' section. Note the syntax for creating integer/string variables and the distinction between val and var.

Now, let's see this in action and also touch on data types. The video below demonstrates this in a REPL (Read-Eval-Print-Loop), which is a great way to experiment.

(12) - Scala for Data Engineering

This video from 'Scala for Data Engineering' provides a practical demonstration of declaring variables and Scala's type system.

Watch from 06:14 to 11:28 to understand Scala's type hierarchy (Any, AnyVal, AnyRef). Then, watch from 13:32 to 19:34 to see a demonstration of val, var, and type inference. Notice how Scala infers the type (Int, Double) from the assigned value, much like Python, but enforces that type at compile time.

Hands-on Practice:

Open the scala-beam-kickstart project from our last lesson in Helix. Create a new file, src/main/scala/Basics.scala. Inside this file, add a main method and experiment with the concepts above.

@main def basics(): Unit = {
  // 1. Declare an immutable integer 'x' with value 10
  val x = 10
  println(s"The value of x is: $x")

  // 2. Try to reassign x. This will cause a compiler error in Helix!
  // x = 20 

  // 3. Declare a mutable string 'y'
  var y = "Hello"
  println(s"y is: $y")

  // 4. Reassign y. This is allowed.
  y = "Hello, Scala!"
  println(s"y is now: $y")

  // 5. Let type inference work for you
  val pi = 3.14159 // Inferred as Double
  val isReady = true // Inferred as Boolean
}

2. Defining Behavior: Functions with def

Defining functions in Scala is syntactically similar to Python's def, but with some key differences that stem from its static typing and functional nature.

Scala Function Syntax Breakdown
This diagram breaks down the syntax of a Scala function. Note the mandatory type annotations for parameters and the optional, but recommended, return type.

Key differences from Python:

  1. Type Annotations: You must specify the type of each parameter.
  2. Return Type: The function's return type is declared after the parameter list. While the compiler can often infer it, explicitly stating it is good practice for clarity.
  3. Implicit Return: The value of the last expression in the function body is automatically the return value. The return keyword is rarely used and is often considered unidiomatic.
  4. No self: For methods inside classes, there is no explicit self parameter like in Python.

Let's see a demonstration.

(12) - Scala for Data Engineering

The following clip demonstrates how to define and call functions in Scala. Pay close attention to the explanation of why the last statement is the return value and the common error of accidentally returning Unit.

Watch from 39:53 to 45:10. This covers the basic syntax, parameter and return types, and the crucial concept of implicit returns.

Hands-on Practice:

Add the following functions to your Basics.scala file and call them from your @main method.

def add(a: Int, b: Int): Int = a + b

def greet(name: String, language: String): String = {
  // A multi-line function with an if/else expression
  val greeting = if (language == "scala") {
    "Greetings,"
  } else {
    "Hello,"
  }
  
  // The last expression is the concatenation, which is returned
  s"$greeting $name" 
}

// In your @main method:
println(add(5, 7)) // Expected: 12
println(greet("Learner", "scala")) // Expected: Greetings, Learner

A Deeper Look: def vs. val functions

In Scala, functions are first-class citizens. You can define them as named methods with def, but you can also treat them as objects assigned to a val. This is analogous to Python's lambda. Understanding this distinction is vital for functional programming and for passing logic into Beam transforms.

Ch01 Ep01: Val vs Def functions in Scala

This short, focused video clarifies the important difference between a method (def) and a function object (val).

Watch from 00:19 to 03:39 to understand val functions as objects, and then from 07:28 to 09:59 to see how def functions are the more idiomatic way to define reusable methods. This will be very useful later on.


3. Core Collections: List, Map, Set, Tuple

This is where the "immutability by default" philosophy becomes most apparent. While Python's standard list, dict, and set are mutable, Scala's default collections are immutable. When you "add" an element to a Scala List, you get a new list with the added element; the original remains untouched.

This is a critical feature for distributed computing, as immutable data structures are inherently thread-safe and prevent side effects, eliminating a whole class of bugs that can occur in parallel processing.

Scala for Python Developers

Let's return to the 'Scala for Python Developers' guide for a direct comparison of collection syntax.

Read the 'Collections classes' section. Pay close attention to the creation syntax for List, Map, Set, and Tuple, and how element access differs (using parentheses () instead of square brackets []).

Let's reinforce this with a live coding session that covers the same ground.

(12) - Scala for Data Engineering

This video walks through creating and using Scala's main collections. It's a good way to see the syntax in a real-time coding context.

Watch these four segments back-to-back. Tuples (23:28 - 28:32): Note the 1-based indexing (_1, _2). Maps (28:44 - 33:16): Note the -> syntax for key-value pairs. Sets (33:16 - 35:55): Note how duplicates are automatically handled. Arrays (35:55 - 39:37): An Array is Scala's primary mutable sequence, analogous to a Java array. It's useful to know but we'll prefer immutable collections.

Hands-on Practice:

Add these examples to your Basics.scala file to solidify your understanding.

// A List of integers (immutable)
val numbers = List(1, 2, 3, 4, 5)

// "Adding" an element returns a NEW list
val newNumbers = 0 :: numbers // `::` is the "cons" operator to prepend
println(s"Original list: $numbers")
println(s"New list: $newNumbers")
println(s"Accessing an element: ${numbers(2)}") // 0-indexed access, prints 3

// A Map from String to Double (immutable)
val cryptoPrices = Map("BTC" -> 60000.0, "ETH" -> 3000.0)
println(s"Price of ETH: ${cryptoPrices("ETH")}")

// A Tuple holding heterogeneous data
val trade = ("BTC/USD", 1.5, 61500.25, "long")
println(s"Accessing the trade size: ${trade._2}") // 1-indexed access

Conclusion

In this lesson, we bridged the gap between your Python expertise and the fundamentals of Scala. We saw how Scala's static types and preference for immutability differ from Python's dynamic, mutable-by-default world.

Key Takeaways:

  • Variables: Use val for immutable data (preferred) and var for mutable data.
  • Functions: Defined with def, require type annotations, and the last expression is the return value.
  • Collections: List, Map, and Set are immutable by default. This is a deliberate design choice that promotes safety in concurrent systems like Dataflow.
  • Syntax: Accessing collection elements uses parentheses () not brackets []. Tuples are 1-indexed (_1, _2, ...).

You now have the foundational vocabulary to understand and write simple Scala programs.

In our next lesson, "Apply common transformations on the Scala Collections API (map, flatMap, filter, fold)," we will explore how to manipulate these immutable collections. This will directly mirror the kind of data transformation logic you'll write in Apache Beam and will feel very similar to using list comprehensions or tools like itertools in Python.