oym
oym

Reputation: 7083

correct usage of scala map

I find myself using the Scala map function on Option values as follows:

optionVal.map( val => {
  doSomethingWith(val)
  doSomethingElseWith(val)
  // etc. (an example could be executing a database query)
})

where optionVal could be for instance:

Option[String]

In other words, I am using it as a way to ensure that the Option is populated with something, and if so to execute a block of statements.

It seems a bit odd and convoluted to me coming from the Java world where would just do an if check on the object (if (someObject != null) { // execute statements} ). Also it seem like it may not be semantically what the map function was intended to do, even though it works. So I wanted to check if this is the proper / idiomatic way to do this in Scala.

Upvotes: 0

Views: 250

Answers (2)

KChaloux
KChaloux

Reputation: 4028

@LimbSoup's answer is definitely appropriate for a case where you're simply calling a side-effectful function with no return type. For the sake of completeness, here are few other ways to work with Option values in Scala. When working with functions that return a value, you've correctly identified one of the more idiomatic methods, using the map function:

val optX : Option[Int] = Some(1)
optX map { _ + 1 }    // returns Some[Int](2)

val optY : Option[Int] = None
optY map { _ + 1 }    // returns None

This behavior extends to other functions like filter and fold. As syntactic sugar, you can also use a for comprehension, the same way you would on a list.

for (x <- optX) yield (x + 1)  // desugars to 'optX.map(_ + 1)'
for (x <- optX) doSomething(x) // desugars to 'optX.foreach(x => doSomething(x))'

If you want to use Options a bit more in the vein of how you would in Java, you would typically write a pattern matching statement, as follows:

optX match {
  case Some(x) => doSomething(x)
  case None    => doSomethingElse()
}

This is good for when you want an if/else sort of behavior, rather than executing or not executing a function based on whether or not you have a value.

When working with a sequence of Options, you can strip out the None values by using flatMap.

// Applies a map to only the types with values
Seq(Some(1), Some(2), Some(3), None, None, None, Some(4)) flatMap { _ + 1 }  // returns Seq(2, 3, 4, 5)

Lastly, you can completely throw caution to the wind and call .get on your Option type. You probably shouldn't, but it's available to you. This will either return the unwrapped value, in case of a 'Some', or throw an exception in case of 'None'.

Upvotes: 0

Michael Zajac
Michael Zajac

Reputation: 55569

If doSomethingwith(value) returns Unit, then you can use foreach:

def doSomethingWith(value: Int): Unit = println(value)

val opt: Option[Int] = Some(1)

opt.foreach{ value =>
    doSomethingWith(value)  // prints the value
}

val noOpt: Option[Int] = None 

noOpt.foreach{ value =>
    doSomethingWith(value)   // Does nothing, because noOpt is empty.
}

If you intend to map (to return it in some way) the value inside the Option, then continue using map.

Mapping an Option to Unit is harmless, but it would make your code more readable to use foreach.

Upvotes: 6

Related Questions