Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the advantage of using Option.map over Option.isEmpty and Option.get?

Tags:

scala

I am a new to Scala coming from Java background, currently confused about the best practice considering Option[T].

I feel like using Option.map is just more functional and beautiful, but this is not a good argument to convince other people. Sometimes, isEmpty check feels more straight forward thus more readable. Is there any objective advantages, or is it just personal preference?

Example:

Variation 1:

someOption.map{ value => 
  {
    //some lines of code
  }
} orElse(foo)

Variation 2:

if(someOption.isEmpty){
  foo
} else{
  val value = someOption.get
  //some lines of code
}

I intentionally excluded the options to use fold or pattern matching. I am simply not pleased by the idea of treating Option as a collection right now, and using pattern matching for a simple isEmpty check is an abuse of pattern matching IMHO. But no matter why I dislike these options, I want to keep the scope of this question to be the above two variations as named in the title.

like image 389
Hengrui Jiang Avatar asked Aug 12 '16 10:08

Hengrui Jiang


People also ask

Why do we use option in Scala?

Scala's Option is particularly useful because it enables management of optional values in two self-reinforcing ways: Type safety – We can parameterize our optional values. Functionally aware – The Option type also provides us with a set of powerful functional capabilities that aid in creating fewer bugs.

What are the two subtypes of Scala's type option t?

An Option[T] can be either Some[T] or None object, which represents a missing value. For instance, the get method of Scala's Map produces Some(value) if a value corresponding to a given key has been found, or None if the given key is not defined in the Map.

What is option map in Scala?

In Scala, map is a higher order function that takes a function f . If an optional value is present, it applies the function f and returns a value wrapped with Some. else (i.e. if an optional value is absent), it returns None // definition of map in Option class.

What is getOrElse in Scala?

As we know getOrElse method is the member function of Option class in scala. This method is used to return an optional value. This option can contain two objects first is Some and another one is None in scala. Some class represent some value and None is represent a not defined value.


2 Answers

Is there any objective advantages, or is it just personal preference?

I think there's a thin line between objective advantages and personal preference. You cannot make one believe there is an absolute truth to either one.

The biggest advantage one gains from using the monadic nature of Scala constructs is composition. The ability to chain operations together without having to "worry" about the internal value is powerful, not only with Option[T], but also working with Future[T], Try[T], Either[A, B] and going back and forth between them (also see Monad Transformers).

Let's try and see how using predefined methods on Option[T] can help with control flow. For example, consider a case where you have an Option[Int] which you want to multiply only if it's greater than a value, otherwise return -1. In the imperative approach, we get:

val option: Option[Int] = generateOptionValue

var res: Int = if (option.isDefined) {
  val value = option.get
  if (value > 40) value * 2 else -1
} else -1

Using collections style method on Option, an equivalent would look like:

val result: Int = option
  .filter(_ > 40)
  .map(_ * 2)
  .getOrElse(-1)

Let's now consider a case for composition. Let's say we have an operation which might throw an exception. Additionaly, this operation may or may not yield a value. If it returns a value, we want to query a database with that value, otherwise, return an empty string.

A look at the imperative approach with a try-catch block:

var result: String = _
try {
  val maybeResult = dangerousMethod()
  if (maybeResult.isDefined) {
    result = queryDatabase(maybeResult.get)
  } else result = ""
}
catch {
  case NonFatal(e) => result = ""
}

Now let's consider using scala.util.Try along with an Option[String] and composing both together:

val result: String = Try(dangerousMethod())
  .toOption
  .flatten
  .map(queryDatabase)
  .getOrElse("")

I think this eventually boils down to which one can help you create clear control flow of your operations. Getting used to working with Option[T].map rather than Option[T].get will make your code safer.

To wrap up, I don't believe there's a single truth. I do believe that composition can lead to beautiful, readable, side effect deferring safe code and I'm all for it. I think the best way to show other people what you feel is by giving them examples as we just saw, and letting them feel for themselves the power they can leverage with these sets of tools.

like image 50
Yuval Itzchakov Avatar answered Nov 15 '22 07:11

Yuval Itzchakov


using pattern matching for a simple isEmpty check is an abuse of pattern matching IMHO

If you do just want an isEmpty check, isEmpty/isDefined is perfectly fine. But in your case you also want to get the value. And using pattern matching for this is not abuse; it's precisely the basic use-case. Using get allows to very easily make errors like forgetting to check isDefined or making the wrong check:

if(someOption.isEmpty){
  val value = someOption.get
  //some lines of code
} else{
  //some other lines
}

Hopefully testing would catch it, but there's no reason to settle for "hopefully".

Combinators (map and friends) are better than get for the same reason pattern matching is: they don't allow you to make this kind of mistake. Choosing between pattern matching and combinators is a different question. Generally combinators are preferred because they are more composable (as Yuval's answer explains). If you want to do something covered by a single combinator, I'd generally choose them; if you need a combination like map ... getOrElse, or a fold with multi-line branches, it depends on the specific case.

like image 39
Alexey Romanov Avatar answered Nov 15 '22 05:11

Alexey Romanov