I originally wrote a long introduction to this tutorial about how to work with the Scala Option/Some/None classes, but I decided to keep that introduction for a future article. For this article I’ll just say:
- idiomatic Scala code involves never using
null
values - because you never use nulls, it’s important for you to become an expert at using
Option
,Some
, andNone
- initially you may want to use
match
expressions to handleOption
values - as you become more proficient with Scala and Options, you’ll find that
match
expressions tend to be verbose - becoming proficient with higher-order functions (HOFs) like
map
,filter
,fold
, and many others are the cure for that verbosity
Given that background, the purpose of this article is to show how to use HOFs rather than match
expressions when working with Option
values.
Sample data
Here are some simple functions and values that will be used in the table that follows:
def p(i: Int): Boolean = i == 0 // A => Boolean def f(i: Int): Int = i * 2 // A => A def fo(i: Int): Option[Int] = Some(i * 2) // A => Option[A]
val option: Option[Int] = Some(1) val none: Option[Int] = None
val default = 1 val defaultSome = Some(1) val stringOption = Option("foo")
In that code, p is a predicate (of type Int => Boolean
), f
is a simple function of type Int => Int
(or more generally, A => A
), fo
is a function that returns an Option (A => Option[A])
, and hopefully the rest of the code makes sense.
Notes about the following examples:
- Because you know what result type you need when working with an Option in a specific situation, the table is sorted by the expression return type, which is shown in the first column
- While my code uses
Int
values, in all but one example you can think of the expressions as using a generic typeA
From ‘match’ expressions to higher-order functions (HOFs)
Here are the examples, grouped by the type the expressions return:
Result Type |
Match Expression | HOF |
---|---|---|
Unit |
option match { |
option.foreach(println) |
A |
option match { |
option.getOrElse(default) |
A |
option match { |
option.map(f).getOrElse(default) |
Option[A] |
option match { |
option.map(f) |
Option[A] |
option match { |
option.orElse(defaultSome) |
Option[A] |
option match { |
option.filter(p) |
Option[A] |
option match { |
option.filterNot(p) |
Boolean |
option match { |
option.forall(p) |
Boolean |
option match { |
option.exists(p) |
Boolean |
option match { |
option.isEmpty |
Boolean |
option match { |
option.isDefined |
Boolean |
option match { |
option.contains(1) |
Int |
option match { |
option.size |
Int |
option match { |
option.count(p) |
Seq , List , etc. |
option match { |
option.toSeq |
Either[Int,Int] |
option match { |
option.toRight(default) |
Either[Int,Int] |
option match { |
option.toLeft(default) |
A or null |
stringOption match { |
stringOption.orNull (only use this for Java APIs that need it) |
Notes
A few notes:
- Note that in the
fold
example its use is fairly consistent with sequences, where a fold takes an initial seed value and a folding function (see my Scala fold and reduce tutorial for more details on those functions) - I put the
null
example in the last row because you should never use that, unless you’re interacting with a Java API that needs it - In the future I’ll include a companion article that explains and organizes these examples in a different way
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
Resources
UPDATE: For more information, see my free “Higher-order functions in Scala 3” training video.
I initially started writing this article based on my own experience, and then was further propelled to investigate it after I saw this tweet by Jon Pretty about using fold instead of match. That led me to this Tony Morris article about HOFs and Option, and most recently I found this video by Marconi Lanna, which helped to round out the last few examples in the table.