Scala Tutorial

โšก Smart Summary

Scala Tutorial introduces a statically typed, JVM-based language that unites object-oriented and functional programming. The guide covers installation, syntax, core features, and frameworks that make Scala ideal for building concurrent, scalable, and concise modern applications.

  • ๐Ÿ”˜ Hybrid Paradigm: Scala fuses object-oriented and functional programming on the JVM, allowing developers to combine immutability, first-class functions, and traditional class hierarchies.
  • โ˜‘๏ธ Concise Syntax: Type inference, anonymous functions, and pattern matching reduce boilerplate compared with Java while keeping the code expressive and readable.
  • โœ… Setup with IntelliJ: Install the Community Edition IDE, add the Scala plugin, and create an Object that extends App to run the first Hello World program.
  • ๐Ÿงช Functional Building Blocks: Lazy evaluation, currying, higher-order functions, and immutability through val support clean and predictable code.
  • ๐Ÿ› ๏ธ Concurrency and Frameworks: Akka actors, Futures, Play, Lift, and Spark make Scala a strong choice for distributed systems, web applications, and data engineering.

Scala Tutorial

What is Scala?

Scala is a statically typed programming language that combines object-oriented and functional programming to improve the scalability of applications. Scala runs primarily on the JVM platform, and developers can also use it to write software for native platforms through Scala-Native, or for the browser through ScalaJs and the JavaScript runtime.

Scala is a scalable language used to build software for multiple platforms, which is exactly how it got its name. The language was designed to solve several of the verbosity issues found in Java while remaining concise. Martin Odersky designed Scala, and the first public release appeared in 2004.

Why learn Scala

Here are the prime reasons for learning the Scala programming language:

  • Scala is easy to learn for object-oriented programmers and Java developers, and it has become one of the popular languages in recent years.
  • Scala offers first-class functions for users.
  • Scala can be executed on the JVM, paving the way for interoperability with other languages.
  • It is designed for applications that are concurrent, distributed, and resilient message-driven, which makes it one of the most in-demand languages of this decade.
  • It is a concise, powerful language that can quickly grow according to the demand of its users.
  • It is object-oriented and includes many functional programming features, giving developers a lot of flexibility in how they write code.
  • Scala offers Duck Typing through structural types.
  • It has less boilerplate than Java.
  • The frameworks Lift and Play, which are written in Scala, continue to mature and grow in popularity.

How to install Scala

To begin writing Scala programs, you need to have it installed on your computer. To do this, visit the official site at https://www.scala-lang.org/download/ to download the latest version of Scala.

Following the link, you are presented with two options for installing Scala. For this Scala tutorial, we will use the IntelliJ IDEA, which provides excellent Scala support out of the box.

How to install Scala

Once you visit the download link, you will find two editions of the IntelliJ IDE.

For this Scala tutorial, we will download the Community Edition, which is free and includes everything you need to write Scala programs.

IntelliJ Community Edition download page

Step 1) Select Community Edition.
On the page, click the dropdown on the Community Edition.

It presents an option to download the IntelliJ IDE together with JBR, which contains a JDK implementation (Java Development Kit) such as OpenJDK that Scala needs to compile and run code.

Download IntelliJ with JBR

Step 2) Run the installation.
Once you download IntelliJ, double-click the installer and follow the on-screen wizard.

IntelliJ installation wizard

Step 3) Select a location.
Choose a location to install the IDE.

Choose installation folder

If you did not download the bundle with the JDK, the installer still prompts you to add the JDK through a checkbox during setup.

JDK checkbox during install

Step 4) Click Next.
Leave the other defaults as they are and click Next.

Installer default options

Scala Hello World Program

Step 5) Click the startup icon.
Once the installation completes, run the IntelliJ IDE by clicking its startup icon in the Start menu like a regular application.

IntelliJ welcome screen

You still need to add the Scala plugin to IntelliJ. Click the dropdown on the configure menu at the bottom right of the screen and select the Plugins option.

Configure menu for plugins

IntelliJ plugins marketplace

On the Marketplace tab, search for Scala. The plugin appears as the first result under the Languages tag.

Step 6) Install the plugin.
Click Install to begin downloading the Scala plugin.

Install Scala plugin

Step 7) Restart the IDE.
After the download completes, you are prompted to restart the IDE so the installed plugin can start working.

Restart IntelliJ prompt

After restarting, you return to the same page as before, but now the Scala plugin is installed and ready.

Step 8) Select Create Project, which leads to a page where you can choose the language for the project.

Create new project in IntelliJ

Step 9) Choose Scala by selecting the Scala checkbox and click Next.

Select Scala language

Step 10) Select a location to save the project file and give the project a name.

Name and locate project

If the directory does not exist, IntelliJ asks for permission to create the folder. Accept and click Finish. You will then be taken to your Scala project, which currently does not contain any Scala code.

It takes some time to load indexes, so do not worry if you cannot interact with the IDE while a progress bar shows at the bottom. The IDE is loading the files required to run Scala and provide autocompletion.

Step 11) Click the Project tab on the left of the IDE and expand it to see the contents of the project.

Expand project tab

At this point, the project is empty and contains only an .idea folder and a hello-world.iml file generated by the IDE. The point of interest is the src folder, which holds the source code for the project. This is where you create your first Scala file.

Step 12) Right-click on src to open a menu and create a new Scala file.

Create a new Scala file

Choose a name for the file. For this Scala tutorial, use hello, then choose Object from the dropdown for the content type of the Scala file.

Choose Object kind

Once you do this, you have a Scala file that contains a singleton object you will use to run the code.

Singleton object created

Now that you have a Scala file with a Hello object, write your first program by extending the object you created using the App keyword.

Extending the object with App tells the compiler which code to run when the program starts. Immediately after extending App, a green arrow appears on the left side, indicating that the program is now runnable.

Run arrow in IntelliJ

Run Hello option

Inside the Hello object, write the function println() to print the text to the console. Run the program by clicking the green arrow.

Clicking the arrow presents the option Run Hello. Selecting it compiles the code, and after a few seconds the program output appears in the IntelliJ console.

Hello World output in console

You have now successfully installed Scala and run your first program.

What you can do with Scala

Scala is widely used across many domains because of its flexibility and JVM interoperability. Common use cases include:

  • Frontend web development with ScalaJS.
  • Mobile development for Android and iOS with Scala Native.
  • Server-side libraries such as HTTP4S, Akka-Http, and the Play Framework.
  • Internet of Things (IoT) applications.
  • Game development.
  • Natural Language Processing using the ScalaNLP suite of libraries.
  • Practicing advanced techniques such as functional programming and object-oriented programming together.
  • Highly concurrent communication applications using actors, a library for the JVM inspired by Erlang.
  • Machine learning with libraries like Figaro for probabilistic programming and Apache Spark for large-scale data processing.

Anonymous Functions

The Scala language supports anonymous functions, also called function literals. Because Scala is a functional language, developers often break large problems into many small tasks and create many functions. To make this easy, Scala allows functions to be instantiated without a name. You can assign them directly to variables or to definitions using def, as shown in the following Scala example:

val multiplyByTwo = (n:Int) => n * 2
def multiplyByThree = (n:Int) => n * 3

You can then use them like regular functions by passing parameters:

multiplyByTwo(3)
//6

multiplyByThree(4)
//12

These methods come in handy when you want clean and concise code. Anonymous functions are useful when defining methods that are small and do not require a lot of code in their body. They are very simple and do not need any ceremony to create.

These methods are not limited to functions with arguments and can be used to instantiate methods that take no arguments.

val sayHello = () => { println("hello") }

Most of these anonymous functions are used in other parts of code where you need a quick function in place. For this reason, they are also referred to as inline functions. Using anonymous functions is a common pattern that appears throughout the collections library to perform quick actions over a collection.

For instance, the filter method takes an inline function to create another collection with only the elements that meet the criteria defined in the anonymous function.

val myList = List(1,2,3,4,5,6,7)

val myEvenList = myList.filter((n: Int) => n % 2 == 0)
//List(2,4,6)

val myOddList = myList.filter((n:Int) => n % 2 != 0)
//List(1,3,5,7)

Here the anonymous functions check whether the value from the list is odd or even and return the matching item.

//the one checking that the value is even
(n: Int) => n % 2 == 0

//the one checking that the value is odd
(n:Int) => n % 2 != 0

In Scala, it is also possible to use wildcards where the anonymous function parameter is not named. For example:

var timesTwo = (_:Int) * 2

timesTwo(5)
//10

In this scenario, you do not name the parameter. The underscore is used to represent it.

Lazy Evaluation

Most languages sequentially evaluate variables and function parameters, one after the other. In Scala, the keyword lazy helps deal with values that you do not want to evaluate until they are referenced.

A variable marked as lazy is not evaluated where it is defined, which is commonly known as eager evaluation. It is only evaluated when it is referenced later in the code.

This can be helpful when evaluating a value would be an expensive computation. If the value is not always needed, you save yourself from running an expensive computation that can slow down the software by making the variable lazy.

lazy val myExpensiveValue = expensiveComputation

def runMethod() = {
    if(settings == true) {
        use(myExpensiveValue)
    } else {
        use(otherValue)
    }
}

This is not the only use case for lazy variables. They also help deal with circular dependencies in code.

If the settings flag is false, you do not need to use myExpensiveValue, which saves you from doing an expensive computation. This helps ensure that users have a smooth experience because resources are not wasted on values that are never read.

Laziness also helps with function arguments, where the arguments are only used when they are referenced inside the function. This concept is called Call-by-name parameters.

def sometimesUsedString(someValue:String, defaultValue: => String) = {
 if(someValue != null) {
   use(defaultValue)
 } else {
   use(someValue)
   }
 }

Many languages use the call-by-value approach to evaluating arguments. A parameter passed through call-by-name is only evaluated when needed inside the function body. Once the value is evaluated, it is stored and can be reused later without re-evaluation, a concept known as memoization.

Type Inference

In Scala, you do not have to declare types for every variable you create because the Scala compiler can infer types from the right-hand side of an expression. This allows your code to be more concise and frees you from writing boilerplate where the expected type is obvious.

var first:String = "Hello, "
var second:String = "World"
var third = first + second
//the compiler infers that third is of type String

Higher-Order Function

A higher-order function is a function that can take functions as arguments and can return a function as a return type. In Scala, functions are considered first-class citizens. Using functions this way enables you to be very flexible in the kinds of programs you can build. You can create functions dynamically and feed functionality dynamically to other functions.

def doMathToInt(n:Int, myMathFunction:Int=>Int): Int = {
    myMathFunction(n)
}

In the function above, you pass in an Int and a function that takes an Int and returns an Int. You can pass in any function of that signature. By signature, we mean a function’s input and output. A signature of Int => Int means that a function takes an Int as input and returns an Int as its output.

A signature of () => Int means that a function takes nothing as its input and returns an Int as its output. An example of such a function is one that generates a random int.

def generateRandomInt() = {
 return scala.util.Random.nextInt()
}

The above function has a signature () => Int.

A function can also have a signature () => Unit. This means the function takes nothing in and does not return a type. The function may instead change some state or perform a predetermined action.

These kinds of methods are not encouraged because they can act as a black box that affects a system in unknown ways. They are also harder to test. Having explicit input and output types enables you to reason about what your function does.

A higher-order function can also return a function. For example, you could create a method that returns a powering function, one that takes a number and applies a power to it.

def powerByFunction(n:Int): Int=>Int = {
  return (x:Int) => scala.math.pow(x,n).toInt
}

The function above takes an Int. Its return type is an anonymous function that takes an Int x and uses it as the argument for the power function.

Currying

In Scala, you can convert a function that takes two arguments into one that takes one argument at a time. When you pass in one argument, you partially apply it and end up with a function that takes the remaining argument to complete the call. Currying lets you build functions by partially adding some arguments.

This is useful for creating functions dynamically before you have a complete set of arguments.

def multiplyTwoNumbers(n:Int)(m:Int): Int = {
  return n * m
}

If you need a function that multiplies by a specific number, you do not need to create another multiplication method. You can apply one argument to the curried function and reuse the result:

def multiplyTwoNumbers(n:Int)(m:Int): Int = {
  return n * m
}

var multiplyByFive = multiplyTwoNumbers(5) _

multiplyByFive(4)

//returns 20

Pattern Matching

Scala has a powerful built-in mechanism that helps you check whether a variable matches certain criteria, much like a switch statement in Java or a chain of if/else statements. The language supports pattern matching, which you can use to check whether a variable is of a particular type. Pattern matching in Scala is powerful and can destructure components that have an unapply method to get the fields you care about directly from the variable being matched.

Scala pattern matching also provides a more pleasant syntax than a switch statement.

myItem match {
  case true => //do something
  case false => //do something else
  case  _ => //if none of the above do this by default
}

You compare the variable to a set of options. When the variable meets the criteria, the expression on the right side of the fat arrow (=>) evaluates and returns as the result of the match.

The underscore catches cases that are not matched in the code, mirroring the behavior of the default case in a switch statement.

class Animal(var legs:Int, var sound:String)
class Furniture(var legs:Int, var color:Int, var woodType:String)

myItem match {
case myItem:Animal => //do something
case myItem:Furniture => //do something else
case _ => //unknown type, do something else
}

In the code above, you can detect the type of the myItem variable and, based on that, branch off to specific code.

Pattern matching checks whether the variable matches each case in order.

The underscore works as a placeholder that matches any condition that the other case statements do not. You take a variable myItem and call the match method.

  • Check whether myItem is true and run logic on the right side of the fat arrow “=>”.
  • Use the underscore to match anything that does not match any of the case statements defined in the code.

With case classes, you can destructure the class to extract fields inside the object.

By using the sealed keyword to define classes, you get the benefit of having the compiler exhaustively check the cases you try to match and warn you if you forget to handle one.

Immutability

It is possible to create values that cannot be changed by other functions in Scala by using the val keyword. This is achieved in Java by using the final keyword. In Scala, you use a val when creating a variable instead of var, which is the alternative for mutable variables.

A variable defined using the val keyword is read-only, whereas one defined with var can be read and changed later by other functions or by the user in the code.

var changeableVariable = 8

changeableVariable = 10
//the compiler does not complain, and the code compiles successfully

println(changeableVariable)
//10

val myNumber = 7

myNumber = 4

//this will not compile

Trying to assign a value to myNumber after declaring it as a val throws a compile-time error: “reassignment to val.”

Why use Immutability?

Immutability helps prevent code and other programmers from changing values unexpectedly, which would lead to unpredictable results. If consumers of a value need to change it, they can instead make a copy. This way, bugs caused by multiple actors changing the same variable are prevented.

Classes and Objects

Objects are real-world entities, and a class is a template that defines objects. Classes have both state and behaviors. The states are either values or variables. The behaviors are the methods in Scala.

The next section shows how to define a class, instantiate it, and use it in Scala.

Here is a class called Rectangle that has two values and two functions. You can also use the parameters l and b directly as fields in the program. You have an object that has a main method and instantiates the class with two values.

Example:

class Rectangle(l: Int, b: Int) {
  val length: Int = l
  val breadth: Int = b
  def getArea: Int = l * b
  override def toString = s"This is a rectangle with length $length and breadth $breadth"
}
object RectObject {
  def main(args: Array[String]) {
    val rect = new Rectangle(4, 5)
    println(rect.toString)
    println(rect.getArea)
  }
}

All fields and methods are public by default in Scala. It is essential to use override because toString is already defined for every object in Scala.

Inheritance

Scala has multiple types of inheritance (single, multi-level, multiple, hierarchical, and hybrid) that share a lot in common with traditional forms found in Java. You can inherit both from classes and from traits. You inherit the members of one class into another class using the keyword extends. This enables reusability.

It is possible to inherit from one class or multiple classes. It is also possible to inherit from subclasses that themselves have their own superclasses, creating a hierarchy of inheritance in the process.

In the Scala example below, the base class is Circle, and the derived class is Sphere. A circle has a value called radius, which is inherited in the Sphere class. The method calcArea is overridden using the keyword override.

Example:

class Circle {
  val radius = 5
  def calcArea = {
    println(radius * radius)
  }
}
class Sphere extends Circle {
 override def calcArea = {
    println(radius * radius * radius)
  }
}
  object SphereObject {
    def main(args : Array[String]) {
      new Sphere().calcArea
    }
  }

Abstraction

In Scala, you can create abstract methods and member fields using abstract classes and traits. Inside abstract classes and traits, you can define abstract fields without implementing them.

Example:

trait MakesSound {
    var nameOfSound:String
    def sound():String
}
abstract class HasLegs(var legs:Int) {
    val creatureName:String

    def printLegs():String = {
        return s"$creatureName has this number of legs: $legs"
    }
}

These fields are implemented by the classes that extend the trait or abstract class. You can use traits to create contracts for what an application should do and then implement those methods later.

trait DatabaseService {
    def addItemName(itemName:String)
    def removeItem(itemId:Int)
    def updateItem(itemId:Int, newItemName:String)
}

This way, you can plan how an application will look without implementing the methods, helping you envision how various methods will appear. It follows a pattern known as programming to abstractions rather than to the actual implementation.

A class prefaced with the abstract keyword can contain both abstract and non-abstract methods. However, multiple inheritance is not supported in abstract classes, so you can extend only one abstract class.

Singleton Objects

A singleton is a class that is instantiated only once in a program. It comes from a popular and useful programming pattern known as the “singleton pattern.” It is useful for creating instances that are meant to be long-lived and commonly accessed throughout the program, where the state is integral to coordinating events in a system. Creating such a class in Scala is easy because Scala provides a simple means of creating singletons using the object keyword.

object UserProfile {
    var userName = ""
    var isLoggedIn:Boolean = false
}

You can then reference this object throughout the program with the guarantee that all parts of the program see the same data because there is only one instance of it.

def getLoggedInStatus():Boolean = {
   return UserProfile.isLoggedIn
}

def changeLoggedInStatus():Boolean = {
    UserProfile.isLoggedIn = !UserProfile.isLoggedIn
    return UserProfile.isLoggedIn
}

The concept of static members does not exist in Scala, which is why you use singleton objects that act like static members of a class.

Implicit Classes

Implicit classes are a feature added in version 2.10 and are used primarily to add new functionality to existing closed classes.

The implicit keyword should be defined in a class, object, or trait. The primary constructor of an implicit class must have exactly one argument in its first parameter list. It may also include an additional implicit parameter list.

In the Scala example below, new functionality to replace vowels of a String with * is added.

object StringUtil {
  implicit class StringEnhancer(str: String) {

    def replaceVowelWithStar: String = str.replaceAll("[aeiou]", "*")
  }
}

You need to import the implicit class in the class where you are using it.

import StringUtil.StringEnhancer

object ImplicitEx extends App {
  val msg = "This is Guru99!"
  println(msg.replaceVowelWithStar)
}

Object-Oriented Programming (OOP) vs. Functional Programming (FP)

In OOP, programs are constructed by grouping data and the functions that operate on that data into highly connected units. Objects carry their data in fields and the methods operate on them. In this style of programming, the main abstraction is the data, because the methods that are created are meant to operate on the data.

Functional programming, on the other hand, separates data and the functions that operate on it. This enables developers to treat functions as the abstraction and driving force when modeling programs.

Scala enables functional programming by treating functions as first-class citizens, allowing them to be passed as values to other functions and returned as values as well. The combination of these two paradigms has made Scala a great choice for building complex software in industries such as data science.

Important frameworks on Scala

Here are some important frameworks for Scala:

  • Play is an open-source web application framework that uses MVC architecture. Released in 2007 and now licensed under Apache, it became one of the most popular frameworks on GitHub by 2013. Companies such as LinkedIn, Walmart, Samsung, and Eero use this framework.
  • Lift is another free web framework written in Scala, launched in 2007. Foursquare uses the Lift framework. It is a high-performing framework that is fast to build with.
  • Akka provides a toolkit for building highly concurrent, distributed, and resilient message-driven applications on the JVM.
  • Cats is a library that provides abstractions for functional programming.
  • Spark is a unified analytics engine for large-scale data processing, widely used in big data and machine learning workflows.

Concurrency support

  • The values in Scala are immutable by default, which makes it very adaptive to concurrent environments.
  • Scala has many features that make it ideal for concurrent applications.
  • Futures and Promises make it easier to process data asynchronously, thus supporting parallelism.
  • Akka is a toolkit that uses the Actor concurrency model. Actors act when they receive messages.
  • Concurrency using threads from Java is also supported in Scala.
  • Stream processing is another great feature that enables continual, real-time processing of data.

Scala has some of the best concurrency libraries in the Java ecosystem:

  • Native Java threads.
  • Fibers from libraries like Vertx.
  • ZIO, a library that has primitives to help deal with concurrency and asynchronous computation.
  • Software Transactional Memory (STM) for transactions.
  • Future, which is built into the Scala language.

Java vs. Scala

Here are the main differences between Java and Scala.

Scala Java
More compact and concise. Comparatively larger chunks of code.
Designed and developed to be both object-oriented and functional. Supports a wide variety of functional programming features such as concurrency and immutability. Originally developed as an object-oriented language and started supporting functional programming features in recent versions. It is still not as strong as Scala for functional programming.
Uses the actor model for supporting concurrency. Uses the conventional thread-based model for concurrency.
Supports frameworks such as Play and Lift. Supports Spring, Grails, and many more.
Supports lazy evaluation. Does not support lazy evaluation natively.
No static members. Contains static members.
Supports operator overloading. Does not support operator overloading.
Compilation of source code is comparatively slow. Compilation of source code is faster than Scala.
Traits act like Java 8 interfaces. Java 8 interfaces try to bridge the gap between classes and interfaces.
Rewriting is sometimes needed when upgrading versions. Rewriting is generally not required between versions.
Type safety provides strong code reliability but no absolute bug-free guarantee. Strong static typing helps reduce many runtime defects.
Has some backward compatibility limits across major versions. Strong long-term backward compatibility.
Operators are treated as method calls. Operators are special syntax and are not method calls.
Supports multiple inheritance using traits. Does not support multiple inheritance using classes; uses interfaces instead.
Code is written in a compact form. Code is written in a more verbose form.
Scala does not contain the static keyword. Java contains the static keyword.

FAQs

Scala has a steeper learning curve than Java because it blends object-oriented and functional styles. However, developers with Java experience usually pick up the basics quickly. Practice with small projects and Scala REPL accelerates learning.

Yes. Scala continues to power large-scale systems at companies using Akka, Spark, and Kafka. Demand remains strong in data engineering, fintech, and distributed systems where JVM performance and functional programming are valuable.

Yes. Modern AI coding assistants understand Scala syntax, type system, and frameworks like Akka and Spark. They help generate boilerplate, suggest functional patterns, explain compiler errors, and refactor code, accelerating development for both beginners and experts.

Scala drives Apache Spark, the leading engine for large-scale data processing in AI pipelines. Libraries like Breeze, Smile, and Figaro support numerical computing, machine learning, and probabilistic modeling, making Scala a strong choice for production data science.

A val creates an immutable binding evaluated immediately. A var creates a mutable binding that can be reassigned. A lazy val defers evaluation until the value is first accessed, then caches the result for subsequent reads.

Summarize this post with: