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. :)
-- Erik
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.
--
Daniel C. Sobral
I travel to the future all the time.
I tried a simple benchmark (since Caliper is missing from the mavenrepo), 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 have benchmarked it with optimize, and there's no change -- if/else
is twice as fast.
-- Drew
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.
-- Erik