Last updated on
Good to understand (week 3)
This Good to understand document provides answers to commonly asked questions.
This document does not contain new material. We encourage you to check out the table of contents and choose the sections that interest you.
Other than that, you are not required to read the entire document, as its content is already covered in the lectures, exercises or labs.
Sections marked with 📘 are alternative explanations of course material that is already covered in lectures, exercises or labs. Sections marked with 🚀 are here to satisfy your curiosity, but you will not be graded on them.
Useful pattern matching syntax 🚀
Wildcard patterns
You can use _
to discard a part of the pattern. E.g.
case Cons(_, Cons(_, tail)) =>
// `_` doesn't capture any value
Given a following function matchHead
:
def matchHead(list: IntList, target: Int) =
list match
case IntCons(head, tail) =>
if head == target then print(s"Head matches: $target :)")
else print(s"Head doesn't match :(")
case IntNil => print(s"Head doesn't match :(")
2025-09-28/code/useful-pattern-matching-sytax.worksheet.sc
Can you rewrite it so it uses wildcard patterns?
Answer
def matchHead(list: IntList, target: Int) =
list match
case IntCons(head, _) =>
if head == target then print(s"Head matches: $target :)")
else print(s"Head doesn't match :(")
case _ => print(s"Head doesn't match :(")
2025-09-28/code/useful-pattern-matching-sytax.worksheet.sc
If guards
Scala lets you add extra conditions in case
branches by using a “pattern guard”, written case pattern if guard
. Both the pattern and the guard need to be satisfied to enter the branch. For example:
case IntCons(head, _) if head >= 0 =>
??? // only lists starting with non-negative number fall here
case _ =>
??? // all else falls hare
Can you rewrite matchHead
above so it takes advantage of if
guards?
Answer
def matchHead(list: IntList, target: Int) =
list match
case IntCons(head, _) if head == target =>
print(s"Head matches: $target :)")
case _ => print(s"Head doesn't match :(")
2025-09-28/code/useful-pattern-matching-sytax.worksheet.sc
Checking for equality
You can also check for equality using pattern matching directly. To do so, either include the target of the comparison in backticks, or use a name that starts with a capital letter.
val apple = "I have an apple"
val Bananas = "I have some bananas"
x match // x has type String here
case `apple` => // matches if x == "I have an apple"
case Bananas => // matches if x == "I have some bananas"
case apple =>
// matches any x
// this will also define a new variable target with value equal to x that will shadow val apple = "I have an apple"
Can you rewrite matchHead
above so it takes advantage of equality checks?
Answer
def matchHead(list: IntList, target: Int) =
list match
case IntCons(`target`, _) => print(s"Head matches: $target :)")
case _ => print(s"Head doesn't match :(")
2025-09-28/code/useful-pattern-matching-sytax.worksheet.sc
or
def matchHead(list: IntList, Target: Int) =
list match
case IntCons(Target, _) => print(s"Head matches: $Target :)")
case _ => print(s"Head doesn't match :(")
2025-09-28/code/useful-pattern-matching-sytax.worksheet.sc
The second version is not recommended, because Target
isn’t a global constant: this violates the Scala convention that variable names start with a lower-case letter. In contrast, names beginning with an upper-case are reserved for types, type parameters, objects, and constants. The only typical use of this feature (matching against the value of an identifier that starts with a capital letter) is constants.
Using val/var
on class
parameter fields. 📘
If nothing is put on a parameter of a non-case-class
, the parameter is are just treated like private val
to the class: you cannot access it from the outside of the class, and the class itself cannot modify it. In the case where it is not used within any methods, Scala can compile it away, not storing the value inside the class at all.
class A(x: Int):
val n = x + 1
val a = A(5)
a.n // 6
a.x // won't compile
If we put var
/val
, then the parameters are also treated as public variables/values, so we can read (and write into, in the case of var
) them from both inside and outside the class.
class A(var x: Int, val y: Int):
val n = x + y
val a = A(5, 6)
a.n // 11
a.y // 6
a.x = 1 // ok
We can also put private
/protected
before a var
/val
, and it should behave as expected.
For case classes
, however, it is a bit different: all parameters are set to be public val
s by default. You can still override this behavior, the same as for non-case-class
.
However, making a constructor parameter of a case class private wouldn’t be safe anyway. Do you see why?
Spoilers!
Even though, we can’t access our private val
directly from the outside, we can still extract it using pattern matching.
case class A(private val secret: String)
val a = A("secret")
a.secret // won't compile
a match
case A(secret) => secret // ok
In general, we should have case class
parameters stay as public val
s, so it agrees with our concept of them being just a named aggregation of the fields it is made of. There are no such restrictions on non-case classes
.