I just listened to the latest podcast and I'm a bit surprised by the
explanation that Dick gave about Scala's Option type, so I thought I'd make
a few comments (and congratulations to Tor and Carl for pressing Dick to
clarify some of the things he said).

First of all, Option's effect is 100% runtime, not static. It's nothing
like @Nonnull or @Nullable. Dick, can you explain why you said that Option
added static checking?

If you want to get an intuition for Option, it's described in details in
the GOF book (1994!) under the name "Null Object Design Pattern" (here is
the wikipedia entry <http://en.wikipedia.org/wiki/Null_Object_pattern>,
although it doesn't mention the GOF book so maybe I'm misremembering).

The idea behind Option is for the program to continue working even if a
method is invoked on a "null" (called "None" in Scala and "Nothing" in
Haskell) object. Obviously, null checks are still present but since they
are hidden inside the Option object, code that operates on these objects
can be oblivious to them. In other words, if f is an Option of any type,
f.foo() will never crash. Either it will actually call foo if there is an
object inside the Option or it will do nothing if there is no object.

Here are some of the problems with this approach.

Let's say that you have an HTTP request object and you want to see if a GET
parameter is present. Since this parameter could not be there, it's
reasonable to use an Option[String]:

class HttpRequest {
  def getParameter(val name: String) : Option[String] { ...


Now that you have this parameter wrapped in an option, you want to pass it
to another method. Either that method takes a String or an Option[String]:

1) def process(val param: String) { ... }
2) def process(val param: Option[String]) { ... }

In the first case, you have two choices: 1a) either you extract that string
from the option or, if that process() method belongs to you and you can
change it, 1b) you can modify its signature to accept an Option[String]
instead of a String (and obviously, you need to change its implementation
as well).

The 1a) case is basically checking against null, like Java. You will do
something like:

Option[String] p = req.getParameter("foo")
p match {
  case Some(s) : process(s)
  case None: _
}

You have gained nothing (I touched on this particular scenario in this blog
post<http://beust.com/weblog/2010/07/28/why-scalas-option-and-haskells-maybe-types-wont-save-you-from-null/>
).

If 1b) is an option (no pun intended) to you, you go ahead and modify your
process() method to accept an Option, and your modified code will probably
have some matching code as shown above. And if you don't do it at this
level, you will, eventually, have to extract that value from the Option.

Scenario 2) is the best of both worlds: you have two methods that
communicate with each other in terms of Option, and this buys you some nice
properties (composability).

As you can see from this short description, the benefit of Option is
directly proportional to how many of your API's support Options. This is
often referred to as the "Midas effect", something that used to plague the
C++ world with the `const` keyword. The direct consequence is that you end
up reading API docs with method signatures that contain a lot of Options in
them.

The problem with this rosy scenario is that you can't ignore the Java
world, and as soon as you try to interoperate with it, you will be dealing
with raw (non Option) values). So the scenario 1) above is, sadly, common.

Another dark side of Options is that they tend to sweep errors under the
rug. You definitely see a lot less NPE's when you use options, because what
is happening is that your code is happily working on nonexistent objects
(by doing nothing) instead of blowing up. That's fine if your logic is
expected to sometimes return nonexistent objects but there are times when
you are dealing with Options that should never contain None. The correct
thing to do here would be to leave the Option world, extract the underlying
object and keep passing this, but then you are back to the interop problems
described above between raw objects and boxed ones.

Debugging applications that have misbehaving Option objects is quite
challenging because the symptoms are subtle. You put a break point into
your code, look into the Option and realize that it's a None while you
would expect a Some. And now, you have to figure out what part of your code
returned a None instead of a Some, and Option doesn't have enough
contextual data to give you this information (some third party libraries
attempt to fix that by providing improved Options, e.g. scalaz's
Validation).

This is why I think of Options as having a tendency to "sweep errors under
the rug".

>From a practical standpoint, I think that the "Elvis" approach which I
first saw in Groovy and which is also available in Fantom and Kotlin is the
best of both worlds. It doesn't offer the nice monadic properties that
Option gives you, but it comes at a much cheaper price and less boiler
plate.

Finally, Option is still an interesting beast to study since it's a gateway
drug to monads. You probably guessed it by now but Option is a monad, and
understanding some of the properties that it exhibits (e.g. you can chain
several options) is a good first step toward getting a good understanding
of the appeal of monads, and from then on, functors and applicatives are
just a short block away.

Dick, would love to get more details on what you were explaining during
that podcast!

-- 
Cédric

-- 
You received this message because you are subscribed to the Google Groups "Java 
Posse" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/javaposse?hl=en.

Reply via email to