
- Scala - Home
- Scala - Overview
- Scala - Features
- Scala - Environment Setup
- Scala - Build Tool (SBT)
- Scala - REPL
- Scala - Dot & Dotty
- Scala - Basic Syntax
- Scala - Hello World Program
- Scala - Identifiers
- Scala - Keywords
- Scala - Comments
- Scala - Code Blocks
- Scala - Semicolon
- Scala - Constructs
- Scala - Expressions
- Scala - Input and Output
- Scala - Optional Braces
- Scala - Underscore (_)
- Data Types and Variables
- Scala - Data Types
- Scala - Type Bounds
- Scala - Context Bound
- Scala - Variances
- Scala - Type Hierarchy
- Scala - Variables
- Scala - Variable Scopes
- Scala - Literals
- Scala - Numeric Types
- Scala - Boolean Types
- Scala - Char Type
- Scala - Unit Types
- Scala - Strings
- Scala - Arrays
- Scala - Null Type
- Scala - Nothing
- Scala - Any Type
- Scala - AnyRef Type
- Scala - Unified Types
- Scala - Dates and Times
- Scala - Ranges
- Scala - Multidimensional Arrays
- Scala - WrappedArray
- Scala - StringBuilder
- Scala - String Interpolation
- Scala - StringContext
- Scala - Type Casting
- Scala var vs val
- Scala Operators
- Scala - Operators
- Scala - Rules for Operators
- Scala - Arithmetic Operators
- Scala - Relational Operators
- Scala - Logical Operators
- Scala - Bitwise Operators
- Scala - Assignment Operators
- Scala - Operators Precedence
- Scala - Symbolic Operators
- Scala - Range Operator
- Scala - String Concatenation Operator
- Scala Conditional Statements
- Scala - IF ELSE
- Scala - IF-ELSE-IF-ELSE Statement
- Scala - Nested IF-ELSE Statement
- Scala Loop Statements
- Scala - Loop Statements
- Scala - while Loop
- Scala - do-while Loop
- Scala - Nested Loops
- Scala - for Loop
- Scala - break Statement
- Scala - yield Keyword
- Scala Classes & Objects
- Scala - Classes & Objects
- Scala - Constructors
- Scala - Auxiliary Constructor
- Scala - Primary Constructor
- Scala - This Keyword
- Scala - Nested Classes
- Scala - Getters and Setters
- Scala - Object Private Fields
- Scala - Singleton Object
- Scala - Companion Objects
- Scala - Creating Executable Programs
- Scala - Stateful Object
- Scala - Enumerations
- Scala - Polymorphism
- Scala - Access Modifiers
- Scala - Apply Method
- Scala - Update Methods
- Scala - UnapplySeq Method
- Scala - Inheritance
- Scala - Extending a Class
- Scala - Method Overloading
- Scala - Method Overriding
- Scala - Generic Classes
- Scala - Generic Functions
- Scala - Superclass Construction
- Scala Methods & Functions
- Scala - Functions
- Scala - Main Methods
- Scala - Functions Call-by-Name
- Scala - Functions with Named Arguments
- Scala - Function with Variable Arguments
- Scala - Recursion Functions
- Scala - Default Parameter Values
- Scala - Functions without Parameters
- Scala - Implicit Parameters
- Scala - Higher-Order Functions
- Scala - Nested Functions
- Scala - Extension Methods
- Scala - Anonymous Functions
- Partially Applied Functions
- Scala - Lazy Val
- Scala - Pure Function
- Scala - Currying Functions
- Scala - Control Abstractions
- Scala - Corecursion
- Scala - Unfold
- Scala - Tail Recursion
- Scala - Infinite Sequences
- Scala - Dynamic Invocation
- Scala - Lambda Expressions
- Scala Collections
- Scala - Collections
- Mutable and Immutable Collections
- Scala - Lists
- Scala - Sets
- Scala - Maps
- Scala - TreeMap
- Scala - SortedMap
- Scala - Tuples
- Scala - Iterators
- Scala - Options
- Scala - Infinite Streams
- Scala - Parallel Collections
- Scala - Algebraic Data Types
- Scala Pattern Matching
- Scala - Pattern Matching
- Scala - Type Patterns
- Scala - Exception Handling
- Scala - Extractors
- Scala - Regular Expressions
- Scala Files I/O
- Scala - Files I/O
- Scala Advanced Concepts
- Scala - Closures
- Scala - Futures
- Scala - Promises
- Scala - Traits
- Scala - Trait Mixins
- Scala - Layered Traits
- Scala - Trait Linearization
- Scala - Sealed Traits
- Scala - Transparent Traits
- Scala - Literal Type Arithmetic
- Scala - Inline keyword
- Scala - Def, Var & Val
- Scala - Dropped Features
- Scala - BDD Testing
Scala - Algebraic Data Types (ADTs)
Algebraic Data Types (ADTs) are used in functional programming for nested and complex data structures in a type-safe manner. There are categories of Algebraic Data Types (ADTs): product types and sum types.
1. Product Types
Product types combine multiple values into a single value. These are called "product types". Because these represent Cartesian products of their component types. For example, case classes in Scala are used to define product types.
Example
Following is the example which shows you how to product type using case class -
case class Person(name: String, age: Int) object Demo { def main(args: Array[String]): Unit = { // The compiler infers the type as Person val person = Person("Zara", 25) println(person) } }
Save the above code in Demo.scala. The following commands are used to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
The above code defines Person case class with name and age fields. The compiler infers its type and initializes the fields when you create instance of Person.
This will produce the following result -
Person(Zara,25)
2. Sum Types
Sum types represent a value that can be one of various different types. These are called "sum types". Because these represent the sum of their component types. For example, sealed traits along with case classes (or case objects) are used to define sum types in Scala.
Sum types are also known as tagged (or disjoint) unions.
Example
Following is the example which shows you how to sum type using sealed trait and case classes -
sealed trait Shape case class Circle(radius: Double) extends Shape case class Rectangle(width: Double, height: Double) extends Shape object Demo { def main(args: Array[String]): Unit = { val shape1: Shape = Circle(5.0) val shape2: Shape = Rectangle(4.0, 6.0) def describeShape(shape: Shape): String = shape match { case Circle(radius) => s"Circle with radius $radius" case Rectangle(width, height) => s"Rectangle with width $width and height $height" } println(describeShape(shape1)) println(describeShape(shape2)) } }
Save the above code in Demo.scala. The following commands are used to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
The above code defines sum type Shape with two possible cases: Circle and Rectangle. The describeShape function uses pattern matching to handle different shapes.
This will produce the following result -
Circle with radius 5.0 Rectangle with width 4.0 and height 6.0
Combining Product and Sum Types
You can combine product and sum types to model more complex and nested data structures.
Example
Following is the example which shows you how to combine product and sum types -
sealed trait Vehicle case class Car(make: String, model: String) extends Vehicle case class Bike(make: String, engineCapacity: Int) extends Vehicle case class Owner(name: String, vehicle: Vehicle) object Demo { def main(args: Array[String]): Unit = { val owner1 = Owner("Nuha", Car("Toyota", "Camry")) val owner2 = Owner("Ayan", Bike("Yamaha", 600)) def describeOwner(owner: Owner): String = owner.vehicle match { case Car(make, model) => s"${owner.name} owns a car: $make $model" case Bike(make, engineCapacity) => s"${owner.name} owns a bike: $make with engine capacity $engineCapacity cc" } println(describeOwner(owner1)) println(describeOwner(owner2)) } }
Command
> scalac Demo.scala > scala Demo
Output
The above code combines product and sum types to define Owner with Vehicle that can be either a Car or a Bike -
This will produce the following result -
Nuha owns a car: Toyota Camry Ayan owns a bike: Yamaha with engine capacity 600 cc
Recursive Algebraic Data Types
Algebraic Data Types (ADTs) can also be recursive. So you can define complex and nested data structures.
Example
Following is the example which shows you how to recursive ADT representing binary tree -
sealed trait Tree[+A] case class Leaf[A](value: A) extends Tree[A] case class Node[A](left: Tree[A], right: Tree[A]) extends Tree[A] object Demo { def main(args: Array[String]): Unit = { def sumTree(tree: Tree[Int]): Int = tree match { case Leaf(value) => value case Node(left, right) => sumTree(left) + sumTree(right) } val tree: Tree[Int] = Node(Leaf(1), Node(Leaf(2), Leaf(3))) val result = sumTree(tree) println(result) } }
Save the above code in Demo.scala. The following commands are used to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
The above code defines recursive binary tree ADT and function to sum the values of the tree.
This will produce the following result -
6
Pattern Matching with ADTs
Pattern matching is a feature in Scala. It works with Algebraic Data Types (ADTs) to handle different cases.
Example
Following is the example which shows you how to use pattern matching with an ADT -
Save the above code in Demo.scala. The following commands are used to compile and execute this program.
sealed trait Result[+A] case class Success[A](value: A) extends Result[A] case class Failure(message: String) extends Result[Nothing] object Demo { def main(args: Array[String]): Unit = { def processResult(result: Result[Int]): String = result match { case Success(value) => s"Success with value: $value" case Failure(message) => s"Failure with message: $message" } val successResult: Result[Int] = Success(42) val failureResult: Result[Int] = Failure("Error occurred") println(processResult(successResult)) println(processResult(failureResult)) } }
Save the above code in Demo.scala. The following commands are used to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
The above code defines generic sum type Result with two cases: Success and Failure. The processResult function uses pattern matching to handle different results.
This will produce the following result -
Success with value: 42 Failure with message: Error occurred
Notes
- Algebraic Data Types (ADTs) combine types to create more complex types.
- Product types combine multiple values into a single value (e.g., case classes).
- Sum types represent a value that can be one of several different types (e.g., sealed traits and case classes).
- ADTs can be recursive. So you can define complex and nested data structures.
- You can combine product and sum types for nested data structures in a type-safe way.
- ADTs reduce the need for explicit type annotations.
- Recursive ADTs can represent hierarchical and nested data, like trees and graphs.