Unit 2: Introduction to Programming in Scala
I. Scala Orientation
Scala is a statically typed, general-purpose programming language created by Martin Odersky and first released publicly in 2004. Its name means “scalable language”: the same language supports concise scripts, object-oriented applications, functional programs, concurrent systems, and distributed frameworks such as Apache Spark.
- Governing principle: Scala combines object-oriented programming, where values and operations are represented through objects, with functional programming, where computation is expressed through functions and immutable data.
- Platform: Scala commonly compiles to Java Virtual Machine (JVM) bytecode, enabling it to use Java libraries such as
java.timeand frameworks such as Hadoop. - Static typing: Types are checked during compilation; for example, assigning
"cluster"to anIntvariable causes a compile-time error. - Concise syntax: Type inference, higher-order functions, and expression-oriented control structures reduce boilerplate without removing type safety.
- Expression orientation: Constructs such as
if,match, and blocks return values; therefore, they can appear on the right side of an assignment. - Programming convention: Immutable values declared with
valare generally preferred over mutable variables declared withvar, especially in concurrent and distributed programs. - Entry point: A traditional executable program defines a
mainmethod or extendsApp; newer Scala versions also support@main.
object ClusterApp {
def main(args: Array[String]): Unit = {
println("Scala on the JVM")
}
}II. Scala Language Foundations — Syntax, Values, and Types
Scala’s language foundations determine how programs represent values, perform operations, and establish types before collection processing or distributed computation begins.
A. Features of Scala
Scala is designed to express complex programs through interoperable, composable, and type-safe abstractions.
- Object-oriented structure: Every value is treated as an object; even
5supports methods such as5.toDouble. - Functional programming: Functions can be stored in variables, passed as arguments, and returned from other functions.
- JVM interoperability: Scala can instantiate Java classes directly, as in
new java.util.ArrayList[String](). - Strong static type system: The compiler prevents incompatible operations, such as adding an
Intdirectly to aBoolean. - Pattern matching:
matchselects behavior by value, type, or structure and is more expressive than a simpleswitch.
val status = 200
val message = status match {
case 200 => "Success"
case 404 => "Not found"
case _ => "Other"
}- Traits: A
traitdefines reusable fields and methods and can be mixed into multiple classes, avoiding the limitations of single implementation inheritance. - Case classes:
case class Node(id: Int, host: String)automatically provides useful methods such asequals,hashCode,toString, andcopy. - Concurrency support: Immutable data and functional transformations reduce shared-state problems in parallel programs.
- Scalability: Operators, classes, functions, and domain-specific APIs allow small expressions such as
data.map(_ * 2)to scale into larger systems.
B. Basic data types and literals used in Scala
Scala provides value types for numbers, characters, and logical values, together with reference types such as strings and collections.
- Integer types:
Byte: 8-bit signed integer, from −128 to 127.Short: 16-bit signed integer.Int: 32-bit signed integer;42is anIntliteral by default.Long: 64-bit signed integer; the suffixLappears in5000000000L.
- Floating-point types:
Float: 32-bit IEEE 754 value;3.5Frequires theFsuffix.Double: 64-bit IEEE 754 value;3.5is aDoubleby default.
- Character and text types:
Charuses single quotes, as in'S', whileStringuses double quotes, as in"Scala". - Boolean type:
Booleanhas only the literalstrueandfalse. - Unit type:
Unitrepresents the absence of a meaningful result; methods returning it use the value(). - Special types:
Anyis the root of Scala’s type hierarchy, whileNothingis the subtype of every type and represents computations that never return normally. - Literal forms: Hexadecimal integers use
0x, such as0xFF; strings may contain escapes such as"\n"or use triple quotes for multiline text. - Interpolation: Prefixing a string with
sinserts expressions marked by$.
val nodes: Int = 8
val load: Double = 0.75
val active: Boolean = true
val label = s"$nodes nodes at $load load"C. Operators and methods used in Scala
Scala operators are method calls written in operator notation, so a + b is generally interpreted as a.+(b).
- Arithmetic operators:
+,-,*,/, and%perform arithmetic;7 / 2produces3because both operands are integers. - Relational operators:
<,<=,>, and>=return aBoolean, as inload >= 0.80. - Equality operators:
==and!=perform null-safe value comparison;eqandnetest reference identity for reference types. - Logical operators:
&&,||, and!implement AND, OR, and NOT;&&and||short-circuit. - Assignment-related operators:
x += 1updates a mutable variable and corresponds conceptually tox = x + 1. - Bitwise operators:
&,|,^, and~operate on integral bit patterns; shifts use<<,>>, and>>>. - Method notation: A method with one parameter may use infix notation, so
1 to 5means1.to(5). - Operator precedence: Precedence is based primarily on the first character of the method name; multiplication binds more tightly than addition in
2 + 3 * 4. - Common methods:
"scala".toUpperCase,42.toString, andList(1, 2).contains(2)show that operations are invoked on values.
val total = 10 + 5 // 10.+(5)
val range = 1 to 4 // 1.to(4)
val valid = total > 10 && range.contains(3)D. Introduction to type inference
Type inference allows the compiler to determine a value’s type from its expression while preserving compile-time checking.
- Local inference: In
val count = 12, the literal causescountto be inferred asInt. - Collection inference:
List(1, 2, 3)becomesList[Int], whileList(1, 2.5)receives a common numeric supertype. - Function inference: The expected context may determine parameter types, as in
List(1, 2).map(x => x * 2), wherexis inferred asInt. - Explicit annotations: A declaration such as
val rate: Double = 2documents intent and widens the integer literal to2.0. - Method boundaries: Public method return types are often written explicitly for clarity, although the compiler can infer many non-recursive method results.
- Parameters: Named method parameters require declared types:
def square(x: Int) = x * x. - Limitations: Inference does not mean dynamic typing; once
val size = 4is inferred asInt, it cannot later hold"four".
val workers = 16 // Int
val names = List("n1", "n2") // List[String]
val doubled = workers * 2 // Int
def utilization(used: Double, total: Double): Double =
used / totalIII. Functions and Computation — Reusable Program Behavior
Scala functions encapsulate transformations and can be composed or supplied to collection operations, making them central to data-parallel programming.
A. Functions in Scala
A function maps input values to an output value and may be defined as a named method, anonymous function, or function-valued variable.
- Named methods:
defintroduces a method with parameter lists and an optional explicit return type.
def add(a: Int, b: Int): Int = a + b- Anonymous functions:
(x: Int) => x * xis a function literal with oneIntparameter and anIntresult. - Function types:
Int => Intdenotes a one-argument function;(Int, Int) => Intdenotes a two-argument function. - Higher-order functions: A function is higher-order when it accepts or returns another function. Collection methods such as
map,filter, andreduceuse this principle. - Shorthand syntax: Placeholder notation replaces simple parameters;
numbers.map(_ * 2)is equivalent tonumbers.map(x => x * 2). - Multiple parameter lists:
def multiply(a: Int)(b: Int) = a * bsupports partial application and currying. - Default and named arguments:
def connect(host: String, port: Int = 7077)permitsconnect(host = "master"). - Recursion: A recursive function calls itself and should have a termination condition;
@annotation.tailrecverifies tail-recursive optimization. - Closures: A function may capture a value from its surrounding scope, such as
val factor = 3; val scale = (x: Int) => x * factor. - Collection example: A transformation pipeline selects even values and squares them.
val data = List(1, 2, 3, 4, 5)
val result = data.filter(_ % 2 == 0).map(x => x * x)
// List(4, 16)IV. Scala Collections — Storage and Data Transformation
Scala collections organize groups of values through common operations such as map, filter, fold, and iteration, while differing in mutability, ordering, lookup behavior, and evaluation strategy.
A. Mutable vs. immutable collections
Immutable collections return new collections after an operation, whereas mutable collections may alter their existing internal state.
-
Immutable collections:
- Default availability:
List,Set, andMapusually refer toscala.collection.immutableimplementations. - Update behavior:
val ys = xs :+ 4creates a new sequence;xsremains unchanged. - Concurrency advantage: Unchanging values can be safely shared among tasks without locks for collection updates.
- Structural sharing: Implementations may reuse unchanged internal nodes, so a new collection does not necessarily copy every element.
- Default availability:
-
Mutable collections:
- Explicit import: Types commonly come from
scala.collection.mutable, such asArrayBufferandHashMap. - Update behavior: Operations including
+=,-=, andupdatemodify the collection. - Use case: Repeated local accumulation may be efficient when mutation is controlled and not shared.
- Risk: Concurrent updates to the same mutable collection may produce races or require synchronization.
- Explicit import: Types commonly come from
val fixed = List(1, 2)
val extended = fixed :+ 3
val changing = scala.collection.mutable.ArrayBuffer(1, 2)
changing += 3B. Lists in Scala
A Scala List is an immutable, ordered, singly linked sequence optimized for access and insertion at its beginning.
- Construction:
List(10, 20, 30)creates aList[Int];Nilrepresents the empty list. - Cons operator:
5 :: List(10, 20)prepends5, producingList(5, 10, 20). - Core components:
headreturns the first element andtailreturns the remaining list, but both require a non-empty list. - Safe inspection:
headOptionreturnsSome(value)orNone, avoiding an exception onNil. - Traversal operations:
map,filter,flatMap, andfoldLeftprocess elements without modifying the original list. - Performance: Prepending with
::is constant time, while indexed access such asitems(500)is linear because links must be traversed. - Pattern matching: A list can be decomposed through
head :: tailandNil.
val nodes = List("n1", "n2", "n3")
val first = nodes.headOption
val upper = nodes.map(_.toUpperCase)C. Maps in Scala
A Map stores key-value associations in which each key identifies at most one value.
- Construction:
Map("master" -> 7077, "worker" -> 8081)creates string keys associated with integer ports. - Lookup:
ports.get("master")returnsOption[Int], making a missing key explicit throughNone. - Direct access:
ports("master")returns the value but throws an exception if the key is absent. - Safe defaults:
ports.getOrElse("history", 18080)supplies18080when"history"is not present. - Immutable update:
ports + ("history" -> 18080)returns a new map; adding an existing key replaces its associated value in the result. - Removal:
ports - "worker"creates a map without that key. - Traversal:
map,foreach, and pattern matching can process key-value pairs.
val loads = Map("node1" -> 0.70, "node2" -> 0.85)
val busy = loads.filter { case (_, load) => load >= 0.80 }
// Map("node2" -> 0.85)D. Streams in Scala
A stream is an immutable sequence whose elements are evaluated lazily, allowing potentially large or infinite data sequences to be represented.
- Lazy evaluation: Elements are computed only when demanded by operations such as
take,head, or conversion to a strict collection. - Infinite sequence:
Stream.from(1)represents all positive integers without constructing them simultaneously. - Construction: In Scala 2,
#::prepends an element lazily andStream.emptyterminates a finite stream. - Memoization: Once evaluated, stream elements are generally retained, which avoids recomputation but may consume memory if references to the beginning remain.
- Finite extraction:
Stream.from(1).map(_ * 2).take(4).toListproducesList(2, 4, 6, 8). - Modern replacement: Scala 2.13 deprecated
Streamin favor ofLazyList, which provides clearer lazy-tail behavior and similar operations. - Cluster relevance: Language-level streams model lazy in-memory sequences; they are distinct from distributed streaming systems such as Spark Structured Streaming.
val naturals = LazyList.from(1)
val multiplesOfFive = naturals.map(_ * 5)
val firstFour = multiplesOfFive.take(4).toList
// List(5, 10, 15, 20)Did this save you a night before the exam?
LPU Notes is free, and it stays free. Ads cover part of the server bill. The rest comes out of a student's own pocket: the domain, the storage, and keeping the site up through the weeks everyone needs it at once.
The payment button didn't load. An ad blocker or a filtered network is the usual reason. to try again.
Nothing here is ever locked, and nothing unlocks. Chip in only if it was worth it. What it pays for →