
- 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
Usages of Underscore (_) in Scala
Underscore (_) character is reserved in Scala. We use Underscore a lot in Scala, which simplifies the code. However, this can cause confusion and make learning more challenging.
1. Pattern Matching and Wildcards
We often use the underscore as a wildcard and for matching unknown patterns when learning Scala.
(a) Module Import
We use underscore when importing packages to import some or all members of the module.
For example,
// Import all members of the scala.util package. import scala.util._ // Import all members of scala.util except for Try. import scala.util.{Try => _, _} // Import all members of scala.util but rename Try to Attempt. import scala.util.{Try => Attempt, _}
(b) Existential Types
We use underscore as wildcard to match all types in type creators. For example, like List, Array, Seq, Option, and Vector.
For example,
def processLists(lists: List[List[_]]): Int = lists.length assertEquals(processLists(List(List(1, 2, 3), List("a", "b")), 2) assertEquals(processLists(List(List(4.0, 5.0), List("x", "y")), 2) assertEquals(processLists(List(List(Array(6), Array(7)), List("p", "q")), 2)
You can get all types of elements in inner list by using underscore(_).
(c) Matching
With the 'match' keyword, we can use the underscore to catch all unhandled cases.
For example, if you want to classify students based on test scores. Then you can use pattern matching for this.
def classifyStudent(score: Int): String = { score match { case x if x >= 90 => "Excellent" case x if x >= 70 && x <= 89 => "Good" case _ => "Needs Improvement" } } classifyStudent(95) // Excellent classifyStudent(80) // Good classifyStudent(60) // Needs Improvement classifyStudent(105) // Excellent
2. Ignoring Things
We can use the underscore to ignore unused variables and types in the code.
(a) Ignored Parameter
We can use underscore (_) to hide unused parameters in function execution.
For example,
val ints = (1 to 5).map(_ => "Int") assertEquals(ints, Vector("Int", "Int", "Int", "Int", "Int"))
By using the underscore, we ignored the values in the map anonymous function. It returns Int for each range element. The anonymized parameter acts as a placeholder in the function. Code cleaner but less explicit:
val numbers = Seq(7.56, 12.34, 9.99) val intParts = numbers.map(_.toInt) assertEquals(intParts, Seq(7, 12, 9))
So, mapping is equivalent to:
numbers.map(x => x.toInt)
We can access nested collections using underscores. For example,
val products = Seq(("book", 15, true), ("pen", 2, false), ("laptop", 900, true), ("notebook", 5, true)) val popularItems = products .filter(_._3) // Filter in only available items (true). .filter(_._2 > 10) // Filter in only items with a price greater than 10. .map(_._1) // Return only the first element of the tuple: the product name. assertEquals(popularItems, Seq("book", "laptop"))
We saw how to hide imports in pattern matching which is similar to ignoring things. We can ignore a specific module and import the rest. For example,
import java.util.{List => _, _} In this example, we import all members from the java.util package except for the List class, which is excluded using 'List => _.
(b) Ignored Variable
We can ignore variables for constructed entries when we don't need them by using the underscore.
For example, we only want the first element in a split string:
val input = "apple,banana,carrot" val Array(first, _*) = input.split(",") assertEquals(first, "apple")
The same applies if we only want the second one:
val input = "apple,banana,carrot" val Array(_, second) = input.split(",") assertEquals(second, "banana")
And can be more than two:
val input = "apple,banana,carrot,dates,eggplant" val Array(first, _*) = input.split(",") assertEquals(first, "apple")
To ignore the entries after the first one, we use the underscore along with *.
We can also randomly ignore any specific entry we do not want using the underscore:
val input = "apple,banana,carrot,dates,eggplant" val Array(a, b, _, d, e) = input.split(",") assertEquals(a, "apple") assertEquals(b, "banana") assertEquals(d, "dates") assertEquals(e, "eggplant")
(c) Variable Initialization to its Default Value
We can use the underscore as a default when the initial value of a variable is not needed:
var count: Int = _ count = 42 println(count) // 42
This does not apply to local variables, they need to be initialized.
3. Conversion
You can use underscore (_) in many ways in conversion.
(a) Function Reassignment (Eta expansion)
You can convert method into function. For example,
def add(a: Int, b: Int): Int = a + b val sum = add _ // reassign add to sum assertEquals(add(5, 7), sum(5, 7))
(b) Variable Argument Sequence
We can convert sequence into variable arguments using 'seqName: _*' with type ascription.
For example,
def product(args: Int*): Int = { args.fold(1)(_ * _) } val factors = Seq(2, 3, 4) val productOfFactors = product(factors: _*) // Convert the sequence factors to varargs using factors: _* assertEquals(productOfFactors, 24)
(c) Partially-Applied Function
We can create a partially-applied function by providing only some arguments and leaving the rest to be passed later.
If parameters are not provided then these are replaced by underscore(_). For example,
def multiply(x: Int, y: Int): Int = x * y val doubleWithFive = multiply(5, _: Int) val result = doubleWithFive(8) assertEquals(result, 40)
You use underscore in partially-applied functions to ignore things, like parameter groups in functions. For example,
def add(x: Int, y: Int)(z: Int, a: Int)(b: Int, c: Int): Int = x + y + z + a + b + c val partialAdd = add(1, 2) _ val result = partialAdd(3, 4)(5, 6) assertEquals(result, 21)
(d) Assignment Operators (Setters overriding)
Overriding the default setter can be seen as a form of conversion using the underscore.
For example,
class Temperature { private var value = 0 def degrees = value def degrees_=(newValue: Int): Unit = { require(newValue >= -20 && newValue <= 40, "Temperature must be between -20 and 40 degrees") value = newValue } } val temp = new Temperature temp.degrees = 25 assertEquals(temp.degrees, 25) try { temp.degrees = 50 // This will fail because 50 is outside the valid temperature range. fail("Invalid temperature setting") } catch { case e: IllegalArgumentException => assertNotEquals(temp.degrees, 50) }
4. Other Usages of Underscore (_) in Scala
These are some other usages of underscore(_) in Scala.
(a) Joining Letters to Punctuation/Operators
Punctuation characters can not be used in variable names like alphanumeric characters. If using punctuation in variable names improves clarity. Then you can do so by joining letters and punctuation with an underscore.
For example,
def multiplyElements(list: List[_]): List[_] = list.map(_ * 2) val multipliedList = multiplyElements(List(3, 6, 9)) assertEquals(multipliedList, List(6, 12, 18))
(b) Numeric Literal Separator
Scala introduced a numeric literal separator with an underscore. For example,
var population = 1_2_3_4_5_6_7_8_9 // 123456789 population = 12_34_567_8_9 // 123456789 population = 123_4_567_89 // 123456789 var price = 2_5_0.5 // 250.5 price = 25_0.5 // 250.5 price = 25_05 // 2505
(c) Higher-Kinded Type
A Higher-Kinded type is a type that abstracts over another type. Scala can generalize type constructors, similar to existential types. For example,
trait OptionContainer[T[_]] { // higher-kinded type parameter def checkIfEmpty(option: T[_]): Boolean } object ListContainer extends OptionContainer[List] { override def checkIfEmpty(option: List[_]): Boolean = option.isEmpty } var listIsEmpty = ListContainer.checkIfEmpty(List(1, 2, 3)) assertTrue(listIsEmpty == false) listIsEmpty = ListContainer.checkIfEmpty(List()) assertTrue(listIsEmpty == true)
We have summarized all other uses of underscore(_) in the following table.
Concept | Example |
---|---|
Existential types | def foo(l: List[Option[_]]) = ... |
Higher kinded type parameters | case class A[K[_],T](a: K[T]) |
Ignored variables | val _ = 5 |
Ignored parameters | List(1, 2, 3) foreach { _ => println("Hi") } |
Ignored names of self types | trait MySeq { _: Seq[_] => } |
Wildcard patterns | Some(5) match { case Some(_) => println("Yes") } |
Wildcard patterns in interpolations | "abc" match { case s"a$_c" => } |
Sequence wildcard in patterns | C(1, 2, 3) match { case C(vs @ _*) => vs.foreach(f(_)) } |
Wildcard imports | import java.util._ |
Hiding imports | import java.util.{ArrayList => _, _} |
Joining letters to operators | def bang_!(x: Int) = 5 |
Assignment operators | def foo_=(x: Int) { ... } |
Placeholder syntax | List(1, 2, 3) map (_ + 2) |
Method values | List(1, 2, 3) foreach println _ |
Converting call-by-name parameters to functions | def toFunction(callByName: => Int): () => Int = callByName _ |
Default initializer | var x: String = _ |