Well, It's late, and I'm tired, and I haven't read the rest of this post yet, so I am sure some of this has been answered. In brief though.
Option is absolutely a static type solution to the problem. Option[String] is not a String. You can't do o.charAt(1) on an Option[String], but you can do an o.map(_.charAt(1)) on it, and it will return a None if it was None to begin with, you can also do stuff like this for (p <- person, a <- p.address, z <- a.zipCode) yield zipCode Which very neatly will handle an optional person, with an optional address, with an optional zipCode, and either return a Some(zipCode) or a None depending on whether person, address or zipCode are actually there (any of them can be None, the result will be None, if they are all there you get a zipCode). Combine that with a loop and a flatten, and you get a very compact way of pulling out all of the zipCodes that are available without a single null check or NPE. Cedric, I invite you to come to our next training session, we have exercises that really demonstrate this stuff very nicely. You say they sweep errors under the rug, but that's a pessimist's viewpoint. From experience on the massive calc engines I work on now, the use of Option and Either are a massive improvement on what was available before. Not every calculation has a valid (or possible) answer. The Java way to handle that is to bail when you get one that doesn't (throw an exception) and you end up with nothing. Now in genetics, where you have millions of calculations, and maybe 5% will fail, using either Option or Either is a far better way to go. Instead you get 95% of the answers completed as expected, and 5% of them have an exceptional outcome which can be reported at the end. When I think of the exception throwing way we are used to, I am pleased to have a much better option. At the end of the day though, the real proof in the pudding (so to speak) as I mentioned on the podcast is that when I started out with Scala I thought the same as you did in your article, that because get could throw an exception, it really didn't give you anything more than null did. It took me a good 6 months or so to realize that I had not had a single NPE in runtime code (or its equivalent get exception I should point out) since I started using option. It makes you think about the fact that the value might not be there when you use the value. In one fell swoop, it has eliminated NPEs from my runtimes - fully a third of my runtime errors at a conservative estimate, not to mention eliminating all of the null checks littered all over my code. I totally stand by what I said on the podcast, and I am happy to discuss it here until the cows come home. The rule of thumb (and it's a simple one) is never use get (unless you know by some means like isDefined, that the value has to be there). Most of the time you leave things in the Option space until the last possible moment, and then provide either a default alternative, or an explanation of why it's missing. The libraries do that too - it's the real value of having something like this as a core feature of the libraries. For extra credit, I highly recommend everyone goes and looks at Either as well - that's a real gem (it has either a left side or a right side - one with the expected answer, one with the exceptional one - at least that's the conventional pattern). You can then map functions over the "right" answer (get it) and leave the left side alone :-) Dick On Sunday, June 3, 2012 7:43:48 PM UTC-7, Cédric Beust ♔ wrote: > > 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 view this discussion on the web visit https://groups.google.com/d/msg/javaposse/-/LLZTNAqqtPAJ. 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.
