This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Using map(...).getOrElse or for/yield/getOrElse instead of if/else, why?

8 replies
Drew
Joined: 2011-12-16,
User offline. Last seen 42 years 45 weeks ago.

Hi Everyone,

I'm relatively new to Scala and I'm trying to figure something out. I
keep seeing that a lot of times a simple "if/else" is replaced with
".map(...).getOrElse" or for/yield/getOrElse (See the following
examples).

I wanted to see what's the advantage of this? At least to me it seems
like it makes the code harder to read and it's probably more
inefficient than a simple if/else.

Thanks,

Drew

[https://github.com/playframework/Play20/blob/master/samples/scala/
zentasks/app/controllers/Projects.scala#L20]

def index = IsAuthenticated { username => _ =>
User.findByEmail(username).map { user =>
Ok(
html.dashboard(
Project.findInvolving(username),
Task.findTodoInvolving(username),
user
)
)
}.getOrElse(Forbidden)
}

[From http://julien.richard-foy.fr/blog/2011/11/26/dependency-injection-in-scala-with-play-2-it-s-free/]

def Authenticated[A](action: Action[A]): Action[A] = action.compose
{ (request, originalAction) =>
(for {
username <- request.session.get("username")
} yield {
originalAction(request)
}) getOrElse {
Unauthorized(views.html.unauthorized())
}
}

d_m
Joined: 2010-11-11,
User offline. Last seen 35 weeks 2 days ago.
Re: Using map(...).getOrElse or for/yield/getOrElse instead of

On Sat, Jan 14, 2012 at 10:51:49PM -0800, Drew wrote:
> I'm relatively new to Scala and I'm trying to figure something out. I
> keep seeing that a lot of times a simple "if/else" is replaced with
> ".map(...).getOrElse" or for/yield/getOrElse (See the following
> examples).
>
> I wanted to see what's the advantage of this? At least to me it seems
> like it makes the code harder to read and it's probably more
> inefficient than a simple if/else.

Hi Drew,

First off, I can say definitely that if you stick with Scala you will
get used to these constructs. They are certainly not used just to make
code harder to read! :)

Rather than try to tackle all the things you mention, I will focus on
your example using Option, map, and getOrElse.

(In my opinion) Scala tries to make it easier to write correct code.
The nice thing about using Option is that (unlike e.g. null) it makes
it very clear when you need to do some checking to see whether you have
a value or not.

Let's say you have:

case class User(name:String)
def getUser(...): Option[User] = ...

I'm assuming you're used to reading code like:

val userOpt:Option[User] = getUser(...)
var name = "unknown"
if (userOpt.isDefined) name = userOpt.get.name

Many advanced Scala users prefer to avoid calling get on an Option.
It's easy to forget to check whether it's defined (or to end up
checking multiple times), and since it will throw exceptions in some
cases the reader has to trust (or verify) that the author knew what
they were doing.

The version people might prefer is:

val name = getUser(...).map(_.name).getOrElse("unknown")

The nice thing about this is that the type system helps you get things
right. If you forget to handle the case where getUser(...) returns None
(e.g. leaving off getOrElse) then name will end up being Option[String]
and things will fail:

val name = getUser(...).map(_.name) // we have an Option[String]

As a reader, when I see map() and getOrElse() being used this way I can
be sure that the author isn't going to be accidentally dereferencing
None accidentally. Furthermore, it's easy to combine these:

val status = for (user <- getUser(...); mood <- getMood(...)) {
yield user.name + " is " + mood
}.getOrElse("no status to report")

As you increase the number of things you need to check, this approach
becomes better and better compared to the "normal if/else way". One
place where I see this is in posting GET/POST variables, where you can
never be sure the client is behaving as you expect and so need to code
everything defensively. This strategy makes it very easy to avoid
accidentally crashing.

Anyway, hope this helps a bit! Keep at it and it'll start to look a bit
more obvious. :)

Jason Zaugg
Joined: 2009-05-18,
User offline. Last seen 38 weeks 5 days ago.
Re: Using map(...).getOrElse or for/yield/getOrElse instead of
On Sun, Jan 15, 2012 at 7:51 AM, Drew <drew [at] venarc [dot] com> wrote:
Hi Everyone,

I'm relatively new to Scala and I'm trying to figure something out. I
keep seeing that a lot of times a simple "if/else" is replaced with
".map(...).getOrElse" or for/yield/getOrElse (See the following
examples).

I wanted to see what's the advantage of this? At least to me it seems
like it makes the code harder to read and it's probably more
inefficient than a simple if/else.

Hi Drew,
A middle ground, that avoids the overhead of map/getOrElse (albeit small) but avoids the risk of an unsafe .get, is pattern matching.
  anOption match {    case Some(value) => f(x)    case None => default  }
For such a simple case, this okay. (Even Martin says so! [1]) As Erik explains, using the higher-order functions (perhaps hidden behind the nice syntax of a for-comprehension) shows it's benefits when you chain together computations that produce optional values.
-jason
[1] http://stackoverflow.com/questions/5328007/why-doesnt-option-have-a-fold-method
dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Using map(...).getOrElse or for/yield/getOrElse instead of

On Sun, Jan 15, 2012 at 04:51, Drew wrote:
> Hi Everyone,
>
> I'm relatively new to Scala and I'm trying to figure something out. I
> keep seeing that a lot of times a simple "if/else" is replaced with
> ".map(...).getOrElse" or for/yield/getOrElse (See the following
> examples).
>
> I wanted to see what's the advantage of this? At least to me it seems
> like it makes the code harder to read and it's probably more
> inefficient than a simple if/else.

I find this kind of code easier to read than if/else, so this is just
a matter of habit.

To put it simply, this code is safer. Compared to the two main alternatives:

* Using null -- pretty much all code can throw a null pointer exception
* Using Option.get -- any use of get may throw an exception
* Using Option.map + .getOrElse -- no where an exception will be
thrown (except for other reasons, of course)

I might add that Option.foreach has the same advantage.

This not only reduces errors writing code, but also makes refactorings
much more resilient, since refactorings tend to move code around. For
the first two methods, the correctness of the code depends on where
the code is. With map/getOrElse, all code that compiles is correct. In
this respect, anyway.

I tried a simple benchmark (since Caliper is missing from the maven
repo), and using map/getOrElse is twice as slow as if/else. Alas,
using match, as suggested by Jason, is as fast as if/else. So, if you
find code like this in a performance-critical path, then, by all
means, optimize it.

Derek Williams 3
Joined: 2011-08-12,
User offline. Last seen 42 years 45 weeks ago.
Re: Using map(...).getOrElse or for/yield/getOrElse instead of
On Sun, Jan 15, 2012 at 8:32 AM, Daniel Sobral <dcsobral [at] gmail [dot] com> wrote:
I tried a simple benchmark (since Caliper is missing from the maven repo), and using map/getOrElse is twice as slow as if/else. Alas,
using match, as suggested by Jason, is as fast as if/else. So, if you
find code like this in a performance-critical path, then, by all
means, optimize it.


I think Scala will optimize it automatically if used with "-optimize", at least the "getOrElse" part, not sure about "map".
--
Derek Williams
dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Using map(...).getOrElse or for/yield/getOrElse instead of

On Sun, Jan 15, 2012 at 14:12, Derek Williams wrote:
> On Sun, Jan 15, 2012 at 8:32 AM, Daniel Sobral wrote:
>>
>> I tried a simple benchmark (since Caliper is missing from the maven
>> repo), and using map/getOrElse is twice as slow as if/else. Alas,
>> using match, as suggested by Jason, is as fast as if/else. So, if you
>> find code like this in a performance-critical path, then, by all
>> means, optimize it.
>>
>
> I think Scala will optimize it automatically if used with "-optimize", at
> least the "getOrElse" part, not sure about "map".

I have benchmarked it with optimize, and there's no change -- if/else
is twice as fast.

Drew
Joined: 2011-12-16,
User offline. Last seen 42 years 45 weeks ago.
Re: Using map(...).getOrElse or for/yield/getOrElse instead of

Thanks for the replies. Seems like with match/case you get both better performance and a nice functional style.

Jon Steelman
Joined: 2008-12-16,
User offline. Last seen 1 year 29 weeks ago.
Re: Using map(...).getOrElse or for/yield/getOrElse instead of
Can map/getOrElse become as fast as if/else in a future Scala or are we forever looking at twice as slow?
Thanks,Jon

On Sun, Jan 15, 2012 at 11:36 AM, Daniel Sobral <dcsobral [at] gmail [dot] com> wrote:
On Sun, Jan 15, 2012 at 14:12, Derek Williams <derek [at] fyrie [dot] net> wrote:
> On Sun, Jan 15, 2012 at 8:32 AM, Daniel Sobral <dcsobral [at] gmail [dot] com> wrote:
>>
>> I tried a simple benchmark (since Caliper is missing from the maven
>> repo), and using map/getOrElse is twice as slow as if/else. Alas,
>> using match, as suggested by Jason, is as fast as if/else. So, if you
>> find code like this in a performance-critical path, then, by all
>> means, optimize it.
>>
>
> I think Scala will optimize it automatically if used with "-optimize", at
> least the "getOrElse" part, not sure about "map".

I have benchmarked it with optimize, and there's no change -- if/else
is twice as fast.

--
Daniel C. Sobral

I travel to the future all the time.

d_m
Joined: 2010-11-11,
User offline. Last seen 35 weeks 2 days ago.
Re: Using map(...).getOrElse or for/yield/getOrElse instead of

On Sun, Jan 15, 2012 at 11:14:51PM -0500, Jon Steelman wrote:
> Can map/getOrElse become as fast as if/else in a future Scala or are we
> forever looking at twice as slow?

Forever is a long time. I would not hazard a guess at the performance
characteristics of e.g. 2.12 or 3.x. That said, I assume it will be
true for 2.10 when it's released.

If you have paths that have to be really fast today, then I guess I'd
use the match syntax for now. But often I don't think this is that big
a deal really: doing lots of loading into and out of options is often
not the fastest strategy to begin with.

My 2 cents at least.

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland