3

Apologies if this is a newbie question... In Scala I understand that it is preferred to use an Option rather than returning null when you have a function which returns an instance but could potentially return nothing. I understand that this makes it better with regards to safety, because you are not passing null references around, and risking NullPointerException somewhere down the line.

However, is there a cleaner way to handle options than using pattern matching? The syntax I end up using is the following:

val optObj : Option[MyObject] = myFunctionThatReturnsOption
optObj match {
  case Some(obj) => {
  //my code using obj
  }

  case None => _
}

In reality all this doing is the equivalent of the Java version:

MyObject obj = myMethodThatCanReturnNull()
if (obj != null) {
  //my code using obj
}

Is there some other way to avoid all this boilerplate in Scala when using Option instead of null references? All I want to do is execute a piece of code as long as the Option contains some object (i.e. is not None).

4
  • Option is not about safety actually; it's about being able to handle missing values more easily. Commented Apr 23, 2014 at 14:49
  • Well yes, but easily is a bit of an ambiguous term. Having if (obj != null) {} else {} is definitely easy (arguably easier). The reason for using Scala is supporting such things in a typesafe and more correct way... :) Commented Apr 23, 2014 at 14:54
  • if not null ... else null is easy if you do it once but not if you have a chain of functions that can return null; futhermore, I'd even say foo.map(_.bar) is easier and shorter than if (foo != null) foo.bar else null, and also much more readable after a while. Commented Apr 23, 2014 at 14:55
  • blog.tmorris.net/posts/scalaoption-cheat-sheet Commented Apr 23, 2014 at 17:57

5 Answers 5

8

Use foreach, getOrElse and/or map if you want to work in a more consistent way. Here's some use cases and what I'd do:

 //I want to get a non-null value and I have a sane default
 val result = myOption getOrElse 3

 //I want to perform some side effecting action but only if not None
 myOption foreach{ value =>
   println(value toString ())
 }
 //equivalently
 for(value <- myOption){
   //notice I haven't used the "yeild" keyword here
 }

 //I want to do a computation and I don't mind if it comes back as an Option
 val result = for(value <- myOption) yield func(value)
 val equivalent = myOption map func

The third example will use map in both cases.

It gets really interesting when you can mix and match things in a "for comprehension" (Google term.) Let's say that func also returns an Option but I only want things working in specific cases:

 val result = for{ 
   value <- myOption if value > 0
   output <- func(value)
 } yield output

Now I get back an Option but only if myOption contained an integer that was greater than zero. Pretty nifty stuff, no?

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks, the for approach (the 2nd one) looks clean too. I dislike the fact that they called the first approach foreach when there is just one value in an Option, makes readability for newbies like me difficult, but anyway at least its cleaner than a pattern match.
@wheaties: I think you forgot to add a flatMap example.
@jbx: it's a known problem (which many don't see as a problem but I do) that for is not really the perfect word for this; Haskell uses the more neutral do instead.
@ErikAllik Oh, yes the Haskell word is definitely a bit more appropriate. Yes, coming from Java which is more verbose but promotes correct naming of both variables and methods, I often find Scala difficult just because of the naming (especially function arguments just named p or z!). Well, I guess its a question of getting used to it too.
Another alternative which is maybe a bit more adequately named is using the exists method too, if the code is returning boolean.
|
3

You can use foreach if you just want to perform some side-effecting operation with the value:

optObj.foreach(obj => {
    //my code using obj
})

if you have some other use case you should use some other method on Option like map, filter or getOrElse.

2 Comments

Oh, for some reason I associated forEach with iterable things like Lists etc., which is why I missed it... Scala's method naming tend to be so obscure sometimes! Yeah getOrElse I am familiar with, but in that case a pattern match would be better (for my case), because there is no way to execute side-effects on the normal case where get returns a value.
The naming is to be consistent with other containers. Think of an Option as being a container that can hold either exactly one or zero items.
1

Of course, the way I usually use options if I only care about present value is foreach:

optObj.foreach { obj => 
 //...
}

Having said this, there are a lot of other options (which @wheaties enlisted) and some people keep battling about the true one.

Comments

1

You can use the flatMap-method pretty well with Option. Like hier:

case class Player(name: String)
def lookupPlayer(id: Int): Option[Player] = {
  if (id == 1) Some(new Player("Sean"))
  else if(id == 2) Some(new Player("Greg"))
  else None
}
def lookupScore(player: Player): Option[Int] = {
  if (player.name == "Sean") Some(1000000) else None
}

println(lookupPlayer(1).map(lookupScore))  // Some(Some(1000000))
println(lookupPlayer(2).map(lookupScore))  // Some(None)
println(lookupPlayer(3).map(lookupScore))  // None

println(lookupPlayer(1).flatMap(lookupScore))  // Some(1000000)
println(lookupPlayer(2).flatMap(lookupScore))  // None
println(lookupPlayer(3).flatMap(lookupScore))  // None

3 Comments

not sure why you're showing the first 3 printlns there... you almost always want flatMap in that case.
@ErikAllik I think he wants to show me the difference between using map and flatMap, i.e. that it extracts the Option
ah well, that's true :)
1

Here's a great reference for Scala best practices regarding options: http://blog.tmorris.net/posts/scalaoption-cheat-sheet/index.html

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.