Optionals are a huge step forward for Java, even if they aren't perfect. They let you write interfaces that say "I might not have an answer and if you don't deal with it that's your problem". That's important.
That they also allow mapping, filtering, etc, isn't about 'removing ifs' or 'hiding ifs' so much as they are about writing more readable code, imho.
Is this perfect and beautiful? Nah. But it's better than the 20 lines of Java 7 code I'd need to do the same thing. I'm able to write simple predicates and mapper functions as class variables, dynamically if I want, and call them in order as I like. It's short, it's descriptive rather than prescriptive. It isolates what I want from how I do it.
Debugging is annoying, yes, but I think there's hope that a good pattern for it will be figured out by the community.
Why is Optional<> better then throwing an exception or returning a null?
The way I see it it's like this:
Optional<Integer> num = getSomeRiskyNumber();
if (!num.isPresent())
return ... code to bubble up a blank optional
vs
Integer num = getSomeRiskyNumber();
if (num == null)
... throw exception or return null
I get that the author has adopted a more functional programming methodology for dealing with their data but for some tasks this isn't acceptable. To just return 1 value you've allocated at least one object (Optional) and make at least 2 function calls on it (isPresent() and get()).
You get `if (value == null)` for free. Throwing exceptions is very heavy and I'd place Optional<> above that but there isn't any way to signify the error that you acctually got. You'd need to make an Optional<Maybe<T>> where Maybe<T> supports error/exceptions. Maybe Maybe<T> holds data and you can extend it with BadMaybe<T> who extends exception or something so you can put that in instead of your value to signal your exception.
I don't see the benifit. Maybe I'm just crazy but `if (v == null)` has all the features Optional has for less of an overhead and less cognitive load. (If you're afraid of NPEs then just document all the return states of your methods and use @nullable to show when you need to check. IIRC IntellJ catches that kind of mistake).
The real problem is branching - when reading code I have to think through two conditional cases.
In this particular example (where you have to validate a client request), I don't see a way out of branching. However I don't think this post has produced the ideal:
JsonParser.parse(request.getBody())
.flatMap(Validator::validate)
.map(ServiceObject::businessLogic)
.flatMap(JsonGenerator::generate)
.match(l -> HttpResponse.internalServerError(l.getMessage()),
r -> HttpResponse.ok(l));
The problem with this is that I have to think through branching all the way through the data flow. However the only function that should branch is validate, to prepare the request to meet the preconditions of the rest of the data flow, all of which should be non-branching.
In other words, I should be able to read this part of the data flow without thinking of branching:
Yes, I've used an if. (If we don't like ifs we can easily get rid of it, of course - but again our problem is branching not the if.)
Why is this objectively better? Because we now have to think about branching wrt to the validation function ONLY. We've minimized where branching matters, and that's solving the core issue.
The point is not the branching (although it makes a good click-bait title), the point is that we can encode the branching possibilities in our type system, rather than in the permutation of values of variables we have laying around.
Three times you introduce possible failure, the original parse as well as each call to flatMap. It flattens each of these possible failures into one, though, so you don't have to think about it!
So if the programmer is sane and doesn't hide side effects in there you only have to think about branching at the pattern match - if any step failed do this, otherwise do this!
I like functional program a lot, but that very first transform of a chain of if's into a horrifying mess makes a pretty good case for using if. In the if case, function2 and function3 were called identically, but in the functional case suddenly things are inconsistent.
They're called identically in the if case, but they're not used identically. One of functions isn't guaranteed to return a value, so it requires an "if x != null". flatMap basically says "this might be null." Part of the point here is that it makes it explicit when something might return null and ensures it's handled properly.
It doesn't take long to get used to that style. It took me maybe three weeks of playing with Java 8 streams in my spare time before I got quite comfortable with it.
It does look nice but does everything have to be done in one line/chained? Are we just showing off? There are times to use it and not.
Clarity I think comes from breaking out branching into small parts. Coding just for one-liners leads to confusion in teams many times for non functional programmers. You might have programmers yak shaving just to cut down on "ifs" because they are harmful now? Same with null types. Some languages are built for it and others aren't.
The example is less lines of code, and functional clean code, but is it easier to expand, follow and use throughout your codebase? I guess that is up to the project/team, I feel like in many cases this outlook could be adding complexity where simplicity does just fine.
I'll take clarity and expressiveness (e.g. how easy it is to deduce intended behaviour) over consistency any day but perhaps I'm missing something? What's the downside of said inconsistency? (Assuming it's trivial to log inputs and outputs of each function call, if needed)
I feel like the article took a surprising and unusual position - "We prefer Java to Clojure now" - but, then, instead of justifying that position, instead showed how Optional lets you write more functional Java.
It's been a long time since I've written any Java, so I wonder, how is this better than Clojure?
Typing. I like Clojure. I've made a few simple libraries for it while learning. What I don't like is making web service contracts in it. The lack of typing makes the code hard to follow once you get past the first handler. Java keeps types around.
True. In practice, though, this isn't a problem. Most Scala code I've worked with just pretends `null` doesn't exist, which is a fair assumption if you're interfacing with well-behaved Scala libraries that never return null.
Kotlin can't abstract over optional types. Scala Option is a monad which let's you do plenty of cool and useful stuff that Kotlin can only dream of. Kotlin solution is actually more complex, because it is baked into the language as a special case with special syntax.
Also, the type seems to be designed oddly inconsistently in java (differences between Optional<Integer> and OptionalInt, obvious applications like tryGet() missed, etc) and I've read somewhere the designers themself discourage it for many use cases. I don't know why this is the case though.
We're using Kotlin extensively at work and a very happy with it. Spending a couple weeks on converting most of our codebases to mostly-Kotlin-little-Java has given a nice boost to developer morale from my PoV.
Isn't this just pushing the "if" into the called functions? E.g.,
x = something that might result in an exception
x = f(x)
Now f has to check whether x contains an exception, and it should return that exception in that case, and otherwise it should just apply the function to the argument.
As nice as it would be to have a method in java that cannot receive a null argument like Result method( Type NOT NULL name ), it isn't there.
I fail to see much difference between an Optional has/doesn't have a value and null. It's just paint and there is no insight into why there is no value. Something like an Expected type that has either a value or the Error/Exception is much more explicit and may let someone do something about it. At least then the user of the method can choose what an appropriate action is with knowledge. But optional and null are the same and give you no more information than a result or that there is no result.
Someone smarter than me can explain why and how, but I had also heard that streams in Java can perform better than equivalent imperative code as well as being null-safer.
This is because the standard library can forego memory allocation for temporary data structures implied in expressions throughout the stream statement. Also, the Java 8 VM can apply other aggressive optimizations to the lambda functions to inline them.
There are a few of these functional style programming APIs for Java. My favorite so far: JavaSlang [1]. Would be interested to see how it compares to the Lambda library mentioned in the article.
So what was the problem with Clojure? The article makes no connection between the demonstration of the benefits of Either and why they ditched Clojure. As a Clojure user I can only imagine that they did not have the competency (hiring problems for example). Why don't you just pick up Kotlin and forget about nulls altogether?
Inventing your own dialect of a language when none of the libraries support it sounds like you are going to be writing a lot of wrappers or reimplementations of things?
I would have really liked to hear what the argument for switching to Java is - over staying on Clojure or switching to a language other than Java.
One could argue that if you mean "map" use "map", not a little dance with a for loop. I'm inclined to say that the same argument could he applied to "if", but without seeing what their particular code looks like I don't know for sure.
Yeah to me the example in the post looks very obtuse and unreadable compared to the standard "if" version. I prefer clarity in my code especially when I have to come back to it in six months to debug it.
> Optional gives us the ability to say “if a value exists, apply this function to it” repeatedly. It also gives us the ability to chain successive calls ...
Java is already laden with a myriad of utilities such as streams and iterators which allow you to bypass some of the null checks when dealing with IO or collections.
If you want to chain calls, you could do that easily by passing "possibly null" returned values to methods with parameters that are marked @NotNull and handling null checks as exceptions down the line instead of inventing the optional type.
[+] [-] mabbo|9 years ago|reply
That they also allow mapping, filtering, etc, isn't about 'removing ifs' or 'hiding ifs' so much as they are about writing more readable code, imho.
Is this perfect and beautiful? Nah. But it's better than the 20 lines of Java 7 code I'd need to do the same thing. I'm able to write simple predicates and mapper functions as class variables, dynamically if I want, and call them in order as I like. It's short, it's descriptive rather than prescriptive. It isolates what I want from how I do it.Debugging is annoying, yes, but I think there's hope that a good pattern for it will be figured out by the community.
[+] [-] gravypod|9 years ago|reply
The way I see it it's like this:
vs I get that the author has adopted a more functional programming methodology for dealing with their data but for some tasks this isn't acceptable. To just return 1 value you've allocated at least one object (Optional) and make at least 2 function calls on it (isPresent() and get()).You get `if (value == null)` for free. Throwing exceptions is very heavy and I'd place Optional<> above that but there isn't any way to signify the error that you acctually got. You'd need to make an Optional<Maybe<T>> where Maybe<T> supports error/exceptions. Maybe Maybe<T> holds data and you can extend it with BadMaybe<T> who extends exception or something so you can put that in instead of your value to signal your exception.
I don't see the benifit. Maybe I'm just crazy but `if (v == null)` has all the features Optional has for less of an overhead and less cognitive load. (If you're afraid of NPEs then just document all the return states of your methods and use @nullable to show when you need to check. IIRC IntellJ catches that kind of mistake).
[+] [-] Retric|9 years ago|reply
[+] [-] wellpast|9 years ago|reply
The real problem is branching - when reading code I have to think through two conditional cases.
In this particular example (where you have to validate a client request), I don't see a way out of branching. However I don't think this post has produced the ideal:
The problem with this is that I have to think through branching all the way through the data flow. However the only function that should branch is validate, to prepare the request to meet the preconditions of the rest of the data flow, all of which should be non-branching.In other words, I should be able to read this part of the data flow without thinking of branching:
So this I believe is objectively better: Yes, I've used an if. (If we don't like ifs we can easily get rid of it, of course - but again our problem is branching not the if.)Why is this objectively better? Because we now have to think about branching wrt to the validation function ONLY. We've minimized where branching matters, and that's solving the core issue.
[+] [-] lkrubner|9 years ago|reply
[+] [-] village-idiot|9 years ago|reply
[+] [-] Tarean|9 years ago|reply
Three times you introduce possible failure, the original parse as well as each call to flatMap. It flattens each of these possible failures into one, though, so you don't have to think about it!
So if the programmer is sane and doesn't hide side effects in there you only have to think about branching at the pattern match - if any step failed do this, otherwise do this!
[+] [-] cjensen|9 years ago|reply
Is this a satire?
[+] [-] chc|9 years ago|reply
[+] [-] Doradus|9 years ago|reply
[+] [-] drawkbox|9 years ago|reply
Clarity I think comes from breaking out branching into small parts. Coding just for one-liners leads to confusion in teams many times for non functional programmers. You might have programmers yak shaving just to cut down on "ifs" because they are harmful now? Same with null types. Some languages are built for it and others aren't.
The example is less lines of code, and functional clean code, but is it easier to expand, follow and use throughout your codebase? I guess that is up to the project/team, I feel like in many cases this outlook could be adding complexity where simplicity does just fine.
[+] [-] village-idiot|9 years ago|reply
If a function could return either T or null, return Optional<T>, and then use Optional::flatMap to join them together.
Pretty straightforward.
[+] [-] maxxxxx|9 years ago|reply
[+] [-] ebola1717|9 years ago|reply
[+] [-] jhomedall|9 years ago|reply
[+] [-] barnabee|9 years ago|reply
[+] [-] kbuchanan|9 years ago|reply
It's been a long time since I've written any Java, so I wonder, how is this better than Clojure?
[+] [-] virmundi|9 years ago|reply
[+] [-] jankotek|9 years ago|reply
- Optional does not protect from NPE, null is still allowed
- It adds extra layer of complexity
- some libraries use it, some do not. it is not enforced
- extra typing, Java does not even have type inference and `val` declaration
- `if` expression in java does not return a value, no pattern matching... again far more typing
- no support for chained call on several nullable fields
I use Kotlin for couple of years. It has nullability baked into type system and enforced by the compiler. And it works with existing java libraries.
It feels like going back 15 years to Java 1.4, when I use Optional in Java8 or Scala.
[+] [-] sid-kap|9 years ago|reply
[+] [-] pkolaczk|9 years ago|reply
[+] [-] xg15|9 years ago|reply
[+] [-] throwawayish|9 years ago|reply
[+] [-] nilved|9 years ago|reply
[+] [-] ajkjk|9 years ago|reply
[+] [-] amelius|9 years ago|reply
[+] [-] beached_whale|9 years ago|reply
I fail to see much difference between an Optional has/doesn't have a value and null. It's just paint and there is no insight into why there is no value. Something like an Expected type that has either a value or the Error/Exception is much more explicit and may let someone do something about it. At least then the user of the method can choose what an appropriate action is with knowledge. But optional and null are the same and give you no more information than a result or that there is no result.
[+] [-] EdSharkey|9 years ago|reply
This is because the standard library can forego memory allocation for temporary data structures implied in expressions throughout the stream statement. Also, the Java 8 VM can apply other aggressive optimizations to the lambda functions to inline them.
[+] [-] iopq|9 years ago|reply
https://bitbucket.org/iopq/fizzbuzz-in-rust/src/bf4968973d73...
[+] [-] vvanders|9 years ago|reply
https://play.rust-lang.org/?gist=3fb51314d7df9249c9f774dde96...
[+] [-] slantedview|9 years ago|reply
[1]: http://www.javaslang.io/
[+] [-] markelliot|9 years ago|reply
[+] [-] edem|9 years ago|reply
[+] [-] unknown|9 years ago|reply
[deleted]
[+] [-] village-idiot|9 years ago|reply
[+] [-] SeriousM|9 years ago|reply
[+] [-] fulafel|9 years ago|reply
I would have really liked to hear what the argument for switching to Java is - over staying on Clojure or switching to a language other than Java.
[+] [-] draw_down|9 years ago|reply
[+] [-] nv-vn|9 years ago|reply
[+] [-] bananaboy|9 years ago|reply
[+] [-] a3n|9 years ago|reply
It sounds like a ternary operator to me.
[+] [-] nurettin|9 years ago|reply
If you want to chain calls, you could do that easily by passing "possibly null" returned values to methods with parameters that are marked @NotNull and handling null checks as exceptions down the line instead of inventing the optional type.
[+] [-] etaty|9 years ago|reply
We should use exception all the time. We should adopt exceptional programming.
null is the type to return, just in case we haven't thrown an exception yet.
Everyone know the exception API, how to throw them, how to catch them, we have to use it all the time. It's great.
We definitely don't need a strong type system, we need an exceptional programming language, everything is an exception.
[+] [-] alexatkeplar|9 years ago|reply
https://gist.github.com/oxbowlakes/970717