31

My code is becoming littered with the following code pattern:

val opt = somethingReturningAnOpt
if (opt.isDefinedAt) {
    val actualThingIWant = opt.get
}

Is there some way to simplify this? (it seems needlessly complex and a code smell). Ideally it would be something like:

if (Some(actualThingIWant) = somethingReturningAnOpt) {
   doSomethingWith(actualThingIWant)
}

Is anything like that possible?

giampaolo
  • 6,906
  • 5
  • 45
  • 73
laurencer
  • 1,680
  • 1
  • 19
  • 29
  • 1
    That looks awfully close to `match` trying to use `if` syntax... any grudge against the former? Also, don't forget that Option is usable in `for` as well... –  Dec 08 '11 at 07:00
  • [This old thread](http://stackoverflow.com/questions/2079170/why-optiont) may be of some help. – missingfaktor Dec 08 '11 at 08:13
  • The for syntax looks almost perfect - but my only qualm is that it is unintuitive and may give the impression to someone else on the codebase that it is looping over something rather than performing a conditional operation. – laurencer Dec 08 '11 at 22:42

4 Answers4

38

Maybe something like this:

somethingReturningAnOpt match {
  case Some(actualThingIWant) => doSomethingWith(actualThingIWant)
  case None =>
}

or as pst suggests:

somethingReturningAnOpt.foreach { actualThingIWant =>
  doSomethingWith(actualThingIWant)
}

// or...

for (actualThingIWant <- somethingReturningAnOpt) {
  doSomethingWith(actualThingIWant)
}
Rogach
  • 26,050
  • 21
  • 93
  • 172
  • 1
    It's worth noting that the for syntax allows to easily chain and compose option call: val optRes = for { res1 <- getOptRes1 ; res2 <- getOptRes2 } yield { doSomething(res1,res2) } /* here, res1 and res2 are not option, but the content, if available, and optRes will be an Option[TypeOfDoSomethingReturn] */ – fanf42 Dec 08 '11 at 08:53
33

The canonical guide to Option wrangling is by Tony Morris.

Ben Regenspan
  • 10,058
  • 2
  • 33
  • 44
Duncan McGregor
  • 17,665
  • 12
  • 64
  • 118
  • 2
    Personally I find case statements to be clearer for most situations; I'm not sure why Tony discourages them. – Dan Burton Dec 08 '11 at 17:14
  • 1
    I happen to agree with both comments! – Duncan McGregor Dec 09 '11 at 00:16
  • This looks like a link only answer. Can you quote from the article, to make it more useful (and bring it up todays site standard)? – Suma Mar 31 '21 at 08:43
  • Frankly, no. But if you want to add value to the world by paraphrasing someone else’s content just so that other people don’t have to click a link, please fill yer boots. – Duncan McGregor Apr 01 '21 at 18:24
10

Or:

somethingReturningAnOpt.map(doSomethingWith(_))

As in in:

val str = Some("foo")
str.map(_.toUpperCase)

... and use flatMap when the result of doSomethingWith is an Option itself.

val index = Option(Map("foo" -> "bar"))
index.flatMap(_.get("whatever"))        // Returns None :-)
index.map(_.get("whatever"))            // Returns Some(None) :-(
Wilfred Springer
  • 10,869
  • 4
  • 55
  • 69
5

The following code cannot do something useful, since after the if, actualThingIWant is not always defined and as such this code will not compile, as long as you try to use actualThingIWant later.

val opt = somethingReturningAnOpt
if (opt.isDefinedAt) {
    val actualThingIWant = opt.get
}

So, you have to provide a default value. This can be achieved with getOrElse:

val thingIWant = opt.getOrElse(myDefaultValue)

Or if you don't want to have actualThingIWant after the body of the if, which means you want to trigger some side-effects only if the option is defined, you can write:

opt.foreach{ thingIWant => 
  println(thingIWant)
}

or a bit shorter

opt.foreach(println)
ziggystar
  • 28,410
  • 9
  • 72
  • 124