Skip to main content
Scala: Introduction to Functional
  1. Posts/

Scala: Introduction to Functional

·1024 words·5 mins·
Roman
Author
Roman
Photographer with MSci in Computer Science and a Home Lab obsession
Table of Contents

This year I joined a startup that uses Scala as their primary language. Though I had some exposure to functional programming during university with OCaml, it’s been quite a while, and I need to refresh my knowledge. I’ve decided to document my learning journey and share the resources I find helpful along the way.


Introduction to Functional Programming
#

Before diving into Scala specifics, let’s understand how functional programming differs from object-oriented languages like Java (my primary language).

According to Functional Programming for Humans - Foundations by Sameer Kumar, there are several core concepts that differentiate functional programming:

Core Concepts
#

1. Pure Functions ✨
#

  • Calling the same function with the same parameters always returns the same output
  • Examples of non-pure functions include counters or random number generators
  • Pure functions make code more predictable and easier to test

Example:

// Pure function
def add(a: Int, b: Int): Int = a + b

// Non-pure function
var counter = 0
def incrementCounter(): Int = {
  counter += 1
  counter
}

2. Immutability 🔒
#

  • Variables cannot be modified after creation
  • Instead of changing existing data, we create new data structures with modifications
  • This allows better reasoning about code behaviour and eliminates a whole class of bugs related to state management

Example:

// Immutable approach
val list = List(1, 2, 3)
val newList = 0 :: list // Creates a new list: List(0, 1, 2, 3)

// Instead of:
var mutableList = scala.collection.mutable.ListBuffer(1, 2, 3)
mutableList.prepend(0) // Modifies original list

3. No Side Effects 🚫
#

  • Functions should not interact with external systems (APIs, databases, file systems)
  • This separation makes testing easier and reasoning about code behaviour more straightforward

Example:

// Function with side effect
def logAndReturn(x: Int): Int = {
  println(s"Processing: $x") // Side effect: printing
  x * 2
}

// Pure function with side effect moved out
def double(x: Int): Int = x * 2
// Caller decides when/if to perform side effect
val result = double(5)
println(s"Processing: $result")

4. Higher-Order Functions 🔝
#

  • Functions are treated as first-class citizens
  • Can be stored in variables
  • Can be passed as arguments to other functions
  • Can be returned from functions
  • Enables powerful patterns like function composition and decorators

Example:

// Function as a variable
val multiply: (Int, Int) => Int = (a, b) => a * b

// Function as a parameter
def applyOperation(a: Int, b: Int, op: (Int, Int) => Int): Int = op(a, b)
applyOperation(5, 3, multiply) // Returns 15
applyOperation(5, 3, (a, b) => a + b) // Returns 8

// Function returning a function
def createMultiplier(factor: Int): Int => Int = (x: Int) => x * factor
val triple = createMultiplier(3)
triple(4) // Returns 12

Getting Started with Scala
#

After understanding the theory, it’s time to explore Scala itself.

Installation 💻
#

To start playing with Scala, I installed Scala CLI:

brew install Virtuslab/scala-cli/scala-cli

This gives you a quick way to run Scala code without setting up a full project.

Learning Resources 📚
#

If you’re coming from Java (like me), these resources are particularly helpful:

  1. A Scala Tutorial for Java Programmers - Official documentation that bridges the gap between Java and Scala
  2. Scala at Light Speed - Great video introduction to Scala concepts
  3. Scala for Java Developers | Scala Documentation - Quick Java vs Scala side-by-side comparison

Algebraic Data Types (ADTs)
#

One of the more powerful concepts in functional programming is Algebraic Data Types. Here are some resources I found helpful:

Sum Types (OR Types) ➕
#

Sum types represent a value that could be one of several options. In Scala, these are typically modeled using sealed traits or enums.

sealed trait Weather
case object Sunny extends Weather
case object Windy extends Weather
case object Rainy extends Weather
case object Cloudy extends Weather
case object Foggy extends Weather

def feeling(w: Weather): String = w match {
  case Sunny => "Oh, it's such a beautiful sunny day :D"
  case Cloudy => "It's cloudy, but at least it's not raining :|"
  case Rainy => "I am very sad. It's raining outside :("
  // Warning: This is incomplete pattern matching and would cause a runtime error
  // if Windy or Foggy were passed in
}

A complete version would handle all cases:

def feeling(w: Weather): String = w match {
  case Sunny => "Oh, it's such a beautiful sunny day :D"
  case Cloudy => "It's cloudy, but at least it's not raining :|"
  case Rainy => "I am very sad. It's raining outside :("
  case Windy => "It's quite breezy outside, hold onto your hat!"
  case Foggy => "I can barely see through this fog"
}

The compiler can warn us about incomplete pattern matching, helping catch potential bugs before runtime.

Product Types (AND Types) ✖️
#

Product types represent values that contain multiple fields. In Scala, these are typically modeled using case classes.

case class ForecastRequest(val latitude: Double, val longitude: Double)

//type ForecastRequest = Long x Long

Hybrid Types (Combinations) 🔄
#

We can combine sum and product types to create powerful data models:

sealed trait ForecastResponse // Sum type
case class Ok(weather: Weather) extends ForecastResponse // Product type
case class Invalid(error: String, description: String) extends ForecastResponse // Product type

// Usage example
def processResponse(response: ForecastResponse): String = response match {
  case Ok(weather) => s"Weather forecast: $weather"
  case Invalid(error, description) => s"Error: $error - $description"
}

Advantages of ADTs 🌟
#

  • Type Safety: Illegal states become unrepresentable in your code
  • Composability: ADTs can be nested within other ADTs
  • Immutability: ADTs are typically immutable, aligning with functional programming principles
  • Separation of Concerns: They contain just data, no behaviour, which makes it easier to structure code
  • Exhaustiveness Checking: The compiler can verify that all cases are handled

Next Steps 🚀
#

For week 2, I plan to:

  1. Dive deeper into Scala’s collections API
  2. Explore functional error handling with Option, Either, and Try
  3. Future type
  4. Learn about effect systems like ZIO
  5. Monads
  6. Polymorphism