class HttpRequest {def getParameter(val name: String) : Option[String] { ...
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.
Option isn't the same as the null object pattern, because null objects get used as if they were real objects whereas code dealing with Options looks different. It has to contain some form of check or lifting of a function.
Option is the wrong type if you think you might ever ask 'why is it None?'. Before I realised that I had my own version in Java that stored a stack trace at the point of creating a None.
--
You received this message because you are subscribed to the Google Groups "Java Posse" group.
To post to this group, send email to java...@googlegroups.com.
To unsubscribe from this group, send email to javaposse+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/javaposse?hl=en.
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, 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: _}
req.getParameter("foo") foreach { process(_) }
req.getParameter("foo") foreach { process }
process(req.getParameter("foo").orNull)
You have gained nothing (I touched on this particular scenario in this blog post).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.
val params = req.getParametersfor{foo <- params get "foo"bar <- params get "bar"baz <- params get "baz"} { process(foo,bar,baz) }
Option isn't the same as the null object pattern, because null objects get used as if they were real objects whereas code dealing with Options looks different. It has to contain some form of check or lifting of a function.
I disagree on the defaults issue. It's better to have an error of some kind that points you directly at the source of a problem than simply a missing word or a nonsensical one.
Dear unknown,
Your bank account null has a $NaN balance.
Thank you,
$DEPT
--
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/-/2IMakRSvHSoJ.
This. Especially the midas problem tells me that Option<T> is not at all a decent substitute for 'null'.I personally feel like types need saner defaults, but they still have to be optional. For example, strings should default to "", lists should default to an immutable empty list, and so on and so forth. This should make it sufficiently less painful to work with variables that can never be null, that you can reasonably expect to default to 'never null' behaviour and use some sort of special marker to indicate that a value is allowed to contain null. It would then be necessary to initialize any field that is consider 'never null', assuming its type has no default empty value.
Also, to do the concept underlying option right, AND introduce it to java (or at least introduce it so that you can interoperate with java), you need not 2 nullities (can-be-null and never-null) but 4 nullities:* RAW - this basically warns on everything and errors on nothing and is analogous to using java.util.List instead of java.util.List<T> in java 1.5+. Needed only for backwards compatibility.* never-null. For example, if you have a List<[never-null] String>, then when you get strings out, you don't need to null-check them (in fact, if you do, the compiler should warn you that you're doing pointless work), and you cannot put strings into this list unless the compiler can confirm that the reference can't possibly be null at this juncture. That, or a runtime check is added which throws an NPE if you attempt to add a null to this thing.* can-be-null. For example, if you have a List<[can-be-null] String>, then when you get strings out, you must null-check them if you try to pass them to things that want [never-null] Strings. On the plus side, you can shove whatever string you want in these, no need for null checks.* dunno-null. This is a special case which is different from the previous case - it means that it is not known if nulls are allowed here or not. If you have a List<[dunno-null] String>, then when you get these out, you have to null-check, and you also can't put nulls in. The great advantage is that if you have a method with a parameter of type List<[dunno-null] String>, you can pass both List<[never-null] String> and List<[can-be-null] String> into it and that will work as expected without heap corruption. This solves Option's major downfall: it is not feasible to write a method, even one that never writes nulls in and always null-checks when reading, which accepts both a List<T> and a List<Option<T>> transparently, if it needs to do things to the inner T (if the inner T is not null, of course).
The above 4 kinds of null are roughly anologous to the way any given type can show up in 4 different styles in generics: List (raw), List<String>, List<? extends String>, List<? super String>.Such a type system would eliminate all runtime bugs associated with NPEs and turn them into compiler errors. The downside is that this is very complicated: We have 4 separate nullities so how do we specify them for each type? Keywords? That might turn out rather wordy. Symbols, like "String?" for can-be-null, "String!" for never-null, and "String" for dunno-null, with raw types impossible (but files compiled on old versions are all raw?) - that's probably gonna look rather ugly, and if the goal is to turn most refs into never-null, "String!" should probably be the default and we need to come up with another symbol to indicate dunno-null.
I have no solution for this dilemma, other than introducing an IDE which exceeds the ascii character set for symbols, and which introduces certain keyboard shortcuts to change nullity. But that's an entirely different can of worms.
--
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/-/t_ihunElI3MJ.
To post to this group, send email to java...@googlegroups.com.
To unsubscribe from this group, send email to javaposse+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/javaposse?hl=en.
The null coalescing operator aka the Elvis operator is a simple shorthand for the traditional approach seen in C-derived languages:String something = something ? something : "default value"; // Ternary operator in C, Java, JavaScript, C#, an many othersval something = something ?: "default value" // Groovy coalescing operator (Elvis Operator)String something = something ?? "default value"; // C# coalescing operatorThe advantage of Scala's Option is that it works properly with map and fold type functions without any special null checking/coalescing syntax.
Fantom and Kotlin (and Haskell) take this much further. By default, references are non-nullable, and the compiler guarantees that they can never contain null values at runtime. When nulls make sense, you can choose to use a nullable reference.IMO, the best is the Fantom/Kotlin/Haskell route. Scala has the second best option that is based on an intentional trade off with better Java interop. A simple null coalescing operator is the weaker solution and that is only slightly better than Java which requires slightly more syntax.
Haskell uses Maybe, which is directly equivalent to Scala's Option. It just does it a bit more elegantly because the lack of subtyping allows for full H-M type inference.
...
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).
...--Cédric
We all know that testing can't cover everything even under ideal circumstances, and even if it did and you caught this in testing it would be better to have a stack trace than an empty string. Wouldn't you agree,?
--
You received this message because you are subscribed to the Google Groups "Java Posse" group.
To post to this group, send email to java...@googlegroups.com.
To unsubscribe from this group, send email to javaposse+unsubscribe@googlegroups.com.
This now has alarm bells going off in my head. These (as described) would only make sense if specified at both declaration AND use site, that's an awful lot of boilerplate and ceremony to be added! We already tried the same thing with checked exceptions, and you know how well that succeeded...
Then there's the issue of subtyping and the LSP to consider.
It's easy enough to say that String is a subtype of both String! and String?, but it massively changes the Java spec (which only allows subtyping through inheritance). It looks like we'd be in a similar position to having Array[Object] being a supertype of Array[T], and the problems that caused. Then you still have the midas problem if you need to pass a String where a String! is demanded. And how does it work inside collections and generics (especially wildcarded ones)? and through reflection?
I have no solution for this dilemma, other than introducing an IDE which exceeds the ascii character set for symbols, and which introduces certain keyboard shortcuts to change nullity. But that's an entirely different can of worms.Not just the IDE. You have javadoc, static analysis tools, code coverage, etc. etc.
It's a bold solution, to be sure. But the work and complexity required to retrofit it look more complicated than Option[T] at this stage. That's before you even consider composability.
On Monday, June 4, 2012 5:09:43 PM UTC+2, KWright wrote:This now has alarm bells going off in my head. These (as described) would only make sense if specified at both declaration AND use site, that's an awful lot of boilerplate and ceremony to be added! We already tried the same thing with checked exceptions, and you know how well that succeeded...Yes, you need to specify both at declaration and use. Like _ANY_ type. Why does that have alarm bells going off in your head?String x = "Hello"; //site 1System.out.println(x);public void println(String in) { ... } //site 2That's perfectly normal, no need for alarm bells.
public static final Map<String, Integer> example = Map.of("a", 1, "c", 2)
Then there's the issue of subtyping and the LSP to consider.No, the 4-nulls thing actually solves this. Technically, you could treat [dunno-null] as "? extends String! super String?" if you really wanted to, but you can simplify matters considerably by taking nullity out of the type inheritance system. String is a type, and where inheritance is concerned, String is just String, and the nullity of it does not enter into the equation whatsoever; String, String!, String?, String[raw], it's all the same to the type system. As in, == equals - the exact same. If there's a mismatch between a type's nullity state and the operation you do on it, then the compiler will emit an error (you're treating a could-be-null as if it's never-null, or you're passing a could-be-null to something that wants a never-null) or a warning (you're doing null-checks on a never-null). This errors-and-warnings system is like a compiler plugin, it has no actual effect on the meaning of any of the code nor of how any of the code is compiled, the ONLY thing that it could possibly cause is errors and warnings. It's analogous to @Override in that sense. Just compiler-checked documentation is all.If you want to be technical about it, nullity is its own tiny little non-expandable strictly and statically defined hardcoded type system. The upshot is that it's all very easy to reason about and there's no risk of conflicting with existing specs. For example, you cannot write both:public static void test1(String? foo) {}public static void test1(String! foo) {}in the same file, but that would have been possible if these were actually different types.
public class Foo { ... }public class Bar extends Foo { ... }public void doSomething(List<? super Bar!> xs)
List<Bar!>List<Bar>List<Foo!>List<Foo>
It's easy enough to say that String is a subtype of both String! and String?, but it massively changes the Java spec (which only allows subtyping through inheritance). It looks like we'd be in a similar position to having Array[Object] being a supertype of Array[T], and the problems that caused. Then you still have the midas problem if you need to pass a String where a String! is demanded. And how does it work inside collections and generics (especially wildcarded ones)? and through reflection?See above - no changes needed whatsoever. There is also no midas problem here; just because I use this equivalent of Option somewhere does NOT mean my code is going to end up with every type in every parameter replaced with Option<T> everywhere! Generalized APIs such as List will most likely roll with dunno-Ts everywhere but this is completely transparent to ALL nullities: You can pass never-nulls, could-be-nulls, and dunno-nulls to such an API and it would all work. Maybe you don't understand the midas problem? With Option, then I start having List<Option<X>> everywhere, severely complicating my API documentation and requiring me to also start using option. The entire _point_ of the 4-nullity concept is that null safety is transparent yet compile-time checked. Contrast to Option, which is compile-time checked but not transparent, and java's system, which is transparent but not compile-time checked.
public String! turtleA() { return turtleB(); }public String turtleB() { return turtleC(); }public String turtleC() { return ...; }... continue until you run out of turtles ...
I have no solution for this dilemma, other than introducing an IDE which exceeds the ascii character set for symbols, and which introduces certain keyboard shortcuts to change nullity. But that's an entirely different can of worms.Not just the IDE. You have javadoc, static analysis tools, code coverage, etc. etc.Nope, those can just use the long-form ASCII symbol that is in the actual source file. It's fine in all such tools, but where it gets real tedious is in day-to-day edit work, but if your IDE can let you enter the appropriate nullity state very easily, and render it in an unobtrusive manner, you've gotten your 90% in and it's fine then.It's a bold solution, to be sure. But the work and complexity required to retrofit it look more complicated than Option[T] at this stage. That's before you even consider composability.Hah, just... no. It is not possible to retrofit java to Option<T> because that is not transparent; APIs are set in stone, you can't change them. 4-nullities is transparent which means that's actually an option, al though it is very complex.
That sounds terrible. How do you work out the original source of nil?
--
You received this message because you are subscribed to the Google Groups "Java Posse" group.
To post to this group, send email to java...@googlegroups.com.
To unsubscribe from this group, send email to javaposse+...@googlegroups.com.
That sounds terrible. How do you work out the original source of nil?
You don't. I've heard from several people that this is the worst thing about writing iOS apps, or Objective-C in general. Silent, delayed, untraceable null errors.
There is value in being *able* to go from null to null (or nil to nil or None to None). The problem is when it's unintentional and that's what we're saying is bad in JQuery and Objective-C, that there's no visual cue that you might be operating on a nil.
Instead you see a nil 5 miles away in code that has been working for years, with no real clue as to where it comes from.
There is value in being *able* to go from null to null (or nil to nil or None to None). The problem is when it's unintentional and that's what we're saying is bad in JQuery and Objective-C, that there's no visual cue that you might be operating on a nil.
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, 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).
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
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, 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).
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.
If you unbox the Option then you don't have an Option any longer and can't really blame Option for what happens next. Option gives you the same choices plus can be used in for-comprehensions like any other monad.
Option gives you the same choices
Option.map returns another Option, which you clearly don't want if you want something to blow up. Instead of option.map(_ + 1) you'd write option.get + 1
If you require that the Option contains a value then you probably didn't want an Option in the first place but a direct (non-null) value.
--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 java...@googlegroups.com.
To unsubscribe from this group, send email to javaposse+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/javaposse?hl=en.
That's one reason why many batch processing systems have ways of rerunning parts of a job.
If you design the system for it, of course it could continue processing and then raise some alert pointing at the problem data.
If you haven't yet considered this failure mode though it's probably best to just stop. If you carry on maybe you're rendering garbage to the screen, allocating buffers many times bigger than you should because you read 4 bytes that don't mean what you think they will..
--
You received this message because you are subscribed to the Google Groups "Java Posse" group.
To post to this group, send email to java...@googlegroups.com.
To unsubscribe from this group, send email to javaposse+unsubscribe@googlegroups.com.
"Going bang" really isn't your friend
JQuery does the same thing: selectors return arrays of matching elements, but if no elements were found, you receive an empty array instead of null. Anyone who thinks this is a better idea hasn't practically worked with the concept.
Failing fast (with an NPE or, in the case above, with a Javascript error regarding 'undefined') saves much more time than silently proceeding with an unexpected result.
--Cédric
Isn't this variation the one we WANT tho - the normal case it returns an array, but it returns EMPTY when nothing is found, rather than null. Given the contract says "i return 0 or more found things" thats preferred IMHO. getElementById() silently returning null however....On 6/06/12 2:57 AM, Cédric Beust ♔ wrote:
JQuery does the same thing: selectors return arrays of matching elements, but if no elements were found, you receive an empty array instead of null. Anyone who thinks this is a better idea hasn't practically worked with the concept.
Failing fast (with an NPE or, in the case above, with a Javascript error regarding 'undefined') saves much more time than silently proceeding with an unexpected result.
val m = Map(1->"a", 2->"b")m(1) // "a"m.get(1) // Some("a")m(3) // Exception!m.get(3) // None
val xs = Seq("a", "b", "c", "d")xs(1) // "a"xs lift 1 // Some("a")xs(99) // Exception!xs lift 99 // None
Nat Price's solution does offer some nice extra goodies though, `otherwise` is very handy for providing default values
On Tue, 05 Jun 2012 21:48:09 +0200, Josh Berry <tae...@gmail.com> wrote:It's easy to contrive that situation too, thinking of a consequentiality so that the second error only happens in consequence of the previous one, or it will happen only after you fix the previous one :-)
It is easy to contrive the situation where you don't want something to
just go bang on the first error, though. Does anyone like the
scenario where you get an error and fix it, only to be shown a new
error that could have been additionally been reported in the first
pass?
But I'd say that both examples are inappropriate. The point is that I want perhaps something that processes the batch up to the end, then I go to the log file and see the NPE where they happened, versus the fact that I have a result set which is apparently ok, but now I have to dig into it to 1) search for errors and then 2) rebuild the sequence of things that happened back to the error.
val surnames: Seq[Option[String]] = ...val default = ""val surnamesForMerge = surnames map {_ getOrElse default}
val surnames: Seq[String] = ...val page1 = surnames filter {_.head.toLower == 'a'} take 50val page1uc = page1 map {_.toUpperCase}
Option is absolutely a static type solution to the problem. Option[String] is not a String.
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.
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.
Here's a thought experiment, I have:public class Foo { ... }public class Bar extends Foo { ... }public void doSomething(List<? super Bar!> xs)What would be a valid argument to doSomething?List<Bar!>List<Bar>List<Foo!>List<Foo>
This is definitely something outside the current spec, so adding it would be a mammoth task - possibly the same order of magnitude as both generics and annotations.
public String! turtleA() { return turtleB(); }public String turtleB() { return turtleC(); }public String turtleC() { return ...; }... continue until you run out of turtles ...Every method below the top one now has to be changed to return a String! (unless you provide some form of nullity cast). Is this not the essence of the midas problem?
Just call .get on the thing, it'll go bang fast enough :)Often though, that simply isn't what you want. If I'm running some form of 5 hour long big data / batch processing task and it goes bang on item 38,976 / 39,207 then I am *NOT* going to be happy.
"Going bang" really isn't your friend if processing large data sets. Especially if you're processing them in parallel, even more especially if the processing involves side effects - at which point you've made your behaviour non-deterministic.
On 5 June 2012 17:25, Cédric Beust ♔ <ced...@beust.com> wrote:
On Tue, Jun 5, 2012 at 9:18 AM, Ricky Clarkson <ricky.clarkson@gmail.com> wrote:
Option gives you the same choicesHow do I get Option to blow up if I try to map on a None?
--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 java...@googlegroups.com.
To unsubscribe from this group, send email to javaposse+unsubscribe@googlegroups.com.
--
Kevin Wright
mail: kevin.wright@scalatechnology.com
gtalk / msn : kev.lee...@gmail.comvibe / skype: kev.lee.wrightsteam: kev_lee_wright"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra
Just call .get on the thing, it'll go bang fast enough :)Often though, that simply isn't what you want. If I'm running some form of 5 hour long big data / batch processing task and it goes bang on item 38,976 / 39,207 then I am *NOT* going to be happy.
"Going bang" really isn't your friend if processing large data sets. Especially if you're processing them in parallel, even more especially if the processing involves side effects - at which point you've made your behaviour non-deterministic.
On 5 June 2012 17:25, Cédric Beust ♔ <ced...@beust.com> wrote:
On Tue, Jun 5, 2012 at 9:18 AM, Ricky Clarkson <ricky.clarkson@gmail.com> wrote:
Option gives you the same choicesHow do I get Option to blow up if I try to map on a None?
--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 java...@googlegroups.com.
To unsubscribe from this group, send email to javaposse+unsubscribe@googlegroups.com.
--
Kevin Wright
mail: kevin.wright@scalatechnology.com
gtalk / msn : kev.lee...@gmail.comvibe / skype: kev.lee.wrightsteam: kev_lee_wright"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra
Could some or all of this be inferred so we can statically detect possible NPEs without having to add punctuation or annotations to our method signatures?
IDEs can already do some of that but can't cross method boundaries as far as I know.
--
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/-/E0lE0_tdsOwJ.
To post to this group, send email to java...@googlegroups.com.
To unsubscribe from this group, send email to javaposse+...@googlegroups.com.
On 5 June 2012 11:05, Reinier Zwitserloot <rein...@gmail.com> wrote:On Monday, June 4, 2012 5:09:43 PM UTC+2, KWright wrote:This now has alarm bells going off in my head. These (as described) would only make sense if specified at both declaration AND use site, that's an awful lot of boilerplate and ceremony to be added! We already tried the same thing with checked exceptions, and you know how well that succeeded...Yes, you need to specify both at declaration and use. Like _ANY_ type. Why does that have alarm bells going off in your head?String x = "Hello"; //site 1System.out.println(x);public void println(String in) { ... } //site 2That's perfectly normal, no need for alarm bells.Not quite. In addition to the regular type system, we also have the shadow type systems of annotations and checked exceptions. This would be a third shadow system and it *would* add complexity.I suspect that Nullity in this form could be internally implemented through annotations to minimise the effort required, yet it would have to follow different rules in the presence of subtyping (more later). In practice, it would feel a lot more like generics - optional, erased, yet still part of the type system.Nullity is close to variance in this regard. With use-site (Java) or declaration-site(C#, Scala) approaches both being possible. If I could statically type Map<K!, V!> when *defining* the class, then using a guava-esque factory we might have:public static final Map<String, Integer> example = Map.of("a", 1, "c", 2)declaration-site nullity. No extra ceremony or boilerplate in the client code :)Then there's the issue of subtyping and the LSP to consider.No, the 4-nulls thing actually solves this. Technically, you could treat [dunno-null] as "? extends String! super String?" if you really wanted to, but you can simplify matters considerably by taking nullity out of the type inheritance system. String is a type, and where inheritance is concerned, String is just String, and the nullity of it does not enter into the equation whatsoever; String, String!, String?, String[raw], it's all the same to the type system. As in, == equals - the exact same. If there's a mismatch between a type's nullity state and the operation you do on it, then the compiler will emit an error (you're treating a could-be-null as if it's never-null, or you're passing a could-be-null to something that wants a never-null) or a warning (you're doing null-checks on a never-null). This errors-and-warnings system is like a compiler plugin, it has no actual effect on the meaning of any of the code nor of how any of the code is compiled, the ONLY thing that it could possibly cause is errors and warnings. It's analogous to @Override in that sense. Just compiler-checked documentation is all.If you want to be technical about it, nullity is its own tiny little non-expandable strictly and statically defined hardcoded type system. The upshot is that it's all very easy to reason about and there's no risk of conflicting with existing specs. For example, you cannot write both:public static void test1(String? foo) {}public static void test1(String! foo) {}in the same file, but that would have been possible if these were actually different types.
Here's a thought experiment, I have:
public class Foo { ... }public class Bar extends Foo { ... }public void doSomething(List<? super Bar!> xs)What would be a valid argument to doSomething?List<Bar!>List<Bar>List<Foo!>List<Foo>
This is definitely something outside the current spec, so adding it would be a mammoth task - possibly the same order of magnitude as both generics and annotations.
It's easy enough to say that String is a subtype of both String! and String?, but it massively changes the Java spec (which only allows subtyping through inheritance). It looks like we'd be in a similar position to having Array[Object] being a supertype of Array[T], and the problems that caused. Then you still have the midas problem if you need to pass a String where a String! is demanded. And how does it work inside collections and generics (especially wildcarded ones)? and through reflection?See above - no changes needed whatsoever. There is also no midas problem here; just because I use this equivalent of Option somewhere does NOT mean my code is going to end up with every type in every parameter replaced with Option<T> everywhere! Generalized APIs such as List will most likely roll with dunno-Ts everywhere but this is completely transparent to ALL nullities: You can pass never-nulls, could-be-nulls, and dunno-nulls to such an API and it would all work. Maybe you don't understand the midas problem? With Option, then I start having List<Option<X>> everywhere, severely complicating my API documentation and requiring me to also start using option. The entire _point_ of the 4-nullity concept is that null safety is transparent yet compile-time checked. Contrast to Option, which is compile-time checked but not transparent, and java's system, which is transparent but not compile-time checked.
public String! turtleA() { return turtleB(); }public String turtleB() { return turtleC(); }public String turtleC() { return ...; }... continue until you run out of turtles ...
Every method below the top one now has to be changed to return a String! (unless you provide some form of nullity cast). Is this not the essence of the midas problem?
Sure. Retrofitting is *always* harder than getting a clean design in the first place. This is why C# forked the collections when they added generics. I also consider Scala's collections to be a similar fork - not just adding option awareness, but also immutability at the root of the hierarchy and all sorts of monadic goodness. Other such examples are Google Guava and the totallylazy library. This can happen at the library level without requiring sweeping changes to the type system in the language spec.I have no solution for this dilemma, other than introducing an IDE which exceeds the ascii character set for symbols, and which introduces certain keyboard shortcuts to change nullity. But that's an entirely different can of worms.Not just the IDE. You have javadoc, static analysis tools, code coverage, etc. etc.Nope, those can just use the long-form ASCII symbol that is in the actual source file. It's fine in all such tools, but where it gets real tedious is in day-to-day edit work, but if your IDE can let you enter the appropriate nullity state very easily, and render it in an unobtrusive manner, you've gotten your 90% in and it's fine then.It's a bold solution, to be sure. But the work and complexity required to retrofit it look more complicated than Option[T] at this stage. That's before you even consider composability.Hah, just... no. It is not possible to retrofit java to Option<T> because that is not transparent; APIs are set in stone, you can't change them. 4-nullities is transparent which means that's actually an option, al though it is very complex.
The class Tobias is talking about (which I can't believe no one mentioned yet, thanks Tobias) is Optional: http://docs.guava-libraries.googlecode.com/git-history/v12.0/javadoc/com/google/common/base/Optional.html
That doesn't stop JdbcTemplate from being used, so it shouldn't and doesn't stop Option.
--
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/-/NR37xDf42BgJ.
--
You received this message because you are subscribed to the Google Groups "Java Posse" group.
Dale
To unsubscribe from this group, send email to javaposse+unsubscribe@googlegroups.com.
myList map { _ map {_.toString} getOrElse "(null)" }
myList map { x => if (null == x) x else x.toString }
myList.flatten
myList filter { _ != null }
ImmutableList.Builder<String> out = ImmutableList.builder<String>for(T x : myList) {out.add((null==x) ? "null" : x.toString)}out.build()
I'd use annotations for this. If you give me an Option<T>, that's not TOO bad, but if you give me i.e. a List<Option<T>>, you're pretty much forcing me to jump through awkward hoops.
On Tuesday, July 3, 2012 10:27:57 AM UTC+2, Dale Wijnand wrote:
I've found it very useful to design my API, making the intent evidentfrom the type signature ("this method might not have a return valuefor you").Unfortunately it's a bit verbose and of limited use in Java than in Scalabut it still beats ambiguity and having to resort to reading the Javadoc(or, even worst, making assumptions)..
Dale
For example, converting said list to a list of strings:myList map { _ map {_.toString} getOrElse "(null)" }compared to a version using nullsmyList map { x => if (null == x) x else x.toString }or if you just want to drop all the Nones:myList.flattensame again, using nulls:myList filter { _ != null }There's no hoopiness from using Options here, not so long as you have lambdas. Now compare a lambda-free version. I'm using Google guava here to maintain immutability characteristics and so keep it a like-for-like comparison. Also using both the enhanced-for notation and the tertiary operator to reduce the boilerplate as much as possible:ImmutableList.Builder<String> out = ImmutableList.builder<String>for(T x : myList) {out.add((null==x) ? "null" : x.toString)}out.build()
Conclusion: If your goal is to avoid jumping through hoops, then picking on Option isn't exactly fertile ground. There are *much* better candidates for improving the developer experience.
On 6 July 2012 11:41, Reinier Zwitserloot wrote:
I'd use annotations for this. If you give me an Option<T>, that's not TOO bad, but if you give me i.e. a List<Option<T>>, you're pretty much forcing me to jump through awkward hoops.
On Tuesday, July 3, 2012 10:27:57 AM UTC+2, Dale Wijnand wrote:
I've found it very useful to design my API, making the intent evidentfrom the type signature ("this method might not have a return valuefor you").Unfortunately it's a bit verbose and of limited use in Java than in Scalabut it still beats ambiguity and having to resort to reading the Javadoc(or, even worst, making assumptions)..
Dale
On the fly I can't think of a reason to return a List<Option<T>>, that's just ridiculous.
To view this discussion on the web visit https://groups.google.com/d/msg/javaposse/-/alKPZIyxOZUJ.
To unsubscribe from this group, send email to javaposse+...@googlegroups.com.
--
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/-/3NZ0gDscvlQJ.
To unsubscribe from this group, send email to javaposse+...@googlegroups.com.
val p = Some(person)p foreach { myMethod }
I like your ideas and wish to subscribe to your newsletter.
--
You received this message because you are subscribed to the Google Groups "Java Posse" group.
To post to this group, send email to java...@googlegroups.com.
To unsubscribe from this group, send email to javaposse+...@googlegroups.com.
Lol, thanks for the vote of confidence!
Been thinking about getting back on the blog scene, even been pushing zeebox into doing something formal, watch this space...
p foreach {myThing}
Even if you're being ironic, this is right, whether or not you meant for it to be :)In the first case, p could just as well be a collection, or a stream, or a future, or almost anything else that can be classed as a functor. A functor is most easily described as "anything with a map operation", and `foreach` is nothing more than a map operation where you discard the results. (You may be more familiar with the term monad, which is most easily described as "an extension of the functor concept that adds the flatMap operation".)Let's consider that p represents a printer attached to your computer. Initially, you represent it as an Option[Printer], later it might be several printers and so you use Set[Printer], still later still it could be networked printers that have a high latency to enumerate and so you use SetOfFutures[Printer] to take advantage of asynchronous logic.In all cases, the central logic remains totally unaltered even as the type of `p` evolves:p foreach {myThing}nullable values just fail to deliver at this level of abstraction.
(note: SetOfFutures isn't a built-in type, but there are techniques to flatten nested monads such as Set[Future[T]] and treat them as the hypothetical SetOfFutures[T]. This sort of composability is one of the driving forces towards using monads in the first place!)
On 8 July 2012 15:09, Reinier Zwitserloot wrote:
I completely agree:p foreach {myThing}is ever so much cleaner and easier to follow compared toif (p != null) {myThing}It's, frankly, a breath of fresh air.
On Friday, July 6, 2012 5:05:51 PM UTC+1, KWright wrote:
More normally, you'd just map over your option:val p = Some(person)p foreach { myMethod }This works precisely because you're keeping the optional nature of such constructs away from your functions, allowing them to just get on with their job ab not have to muck about checking for null and suchlike.You DON'T weave monads through functions, you weave functions through monads. This is the crucial distinction between Option/Maybe and nullability, but it can also be a tricky concept until your default mental model of functions is that they're first-class entities in their own right, and can be freely composed, manipulated, passed as arguments, etc.So it's your Option[Person] that accepts your method. Not the other way around.
On 6 July 2012 16:47, Cédric Beust ♔ wrote:
--
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/-/1IQ5RGMlnd4J.
To unsubscribe from this group, send email to javaposse+...@googlegroups.com.
for {a <- methodThatMayProduceAb <- tryToGetBFrom(a)} {doSomethingWith(b)}
final A a = getA();if (a != null) {final B b = getBFrom(a);if (b != null) {doSomethingWithB(b);} else {doSomethingIfBWasAbsent();}} else {doSomethingIfAWasAbsent();}
final A a = getA();(a == null) ? doSomethingIfAWasAbsent() : {final B b = getBFrom(a);(b == null) ? doSomethingIfBWasAbsent() : {doSomethingWithB(b);}};
val a = getAval b = a flatMap {getBFrom}a getOrElse {doSomethingIfAWasAbsent()}b getOrElse {doSomethingIfBWasAbsent()}b forEach {doSomethingWithB}
Kevin, I believe you don't need any of the (){} characters there, but maybe I'm missing something.
--
You received this message because you are subscribed to the Google Groups "Java Posse" group.
I meant in the Scala code but forgot to say so. Also, blocks can't be part of expressions in Java like in Scala so your Java code won't compile.
Without a => it's not a closure. I find it confusing sometimes to have extra punctuation; I keep thinking I'm missing something. return (x + 5); bugs me in Java too :)
val b = a flatMap {getBFrom}
val b = a flatMap {getBFrom(_)}
val b = a flatMap {x => getBFrom(x)}
1 to 10 map { println("Yo"); println }
Yo gets printed once, println happens 10 times. Just because you're providing a function doesn't mean you're in a closure. If it was a closure (and certain other magic happened to make it well-typed) you'd see Yo 10 times with a blank line between each.
def foo(): Unit = {
var y = 01 to 10 map {y+= 1; println}println(y)}
foo()System.setOut(someOtherPrintStream)foo()
Like I said, without a => there's no closure.
The for comprehension works because it gets converted into code containing a =>.
A block in a position where a function is expected is just a block whose value needs to be a function. Does that make sense now? I can probably find some specspeak that explains it better if not.
We've already got enough definitions of closures flying around without also calling all functions closures. Why not be precise and say that you have a method that takes a function as a parameter?
You can provide that function via any expression that yields a function, including but not limited to a lambda expression (one containing =>). A lambda expression can close over its scope, i.e., forming a closure, meaning that any values, mutated or otherwise, in its scope can be seen from within the lambda expression.
The takeaway is that you don't need to know any of this crap (and a lot of other crap we all already know from Java) if you're not performing side effects.
The takeaway is that you don't need to know any of this crap (and a lot of other crap we all already know from Java) if you're not performing side effects.