9 ms·
Java Without If
- dudul 10y agoIt's kind of cute to see Java practitioners discover what has been around in many functional languages for literally decades :) But, hey, if it helps make Java code cleaner and safer (and Optional and Either definitely help a lot with that), I'm all for it.
- jfoutz 10y agoThe oldest version I can think of was Konrad Zeus's architecture for real computers. Sigma with index variables forcing an expression to zero capture the same concept. Pretty sure that is an old old idea. Who did it first can be interesting, but it's rarely much use.
- ajkjk 10y agoIs there a non-traumatizing way to debug long chains of method calls? I had a lot of trouble with this when I last used Java, a year or so ago.
- glibgil 10y agoSet a breakpoint in first expression in the function that is being mapped unto?
- ajkjk 10y agoThat's the traumatizing way. Not bad if you have one mapped function. Terrible if you have dozens. Stepping through code should not require setting more than one breakpoint - but if I have to find where a set of chained method calls goes wrong I have to breakpoint-binary-search to the problem. When you have "if" statements, you can press "next" repeatedly in Eclipse/IntelliJ/whatever and step through the code. When you use chained method calls you're constantly going into and out of function calls, and all the local variables become return values that you don't ever necessarily get to see in a stack frame, and sometimes you have to step through loads of boilerplate to see the next interesting line of code, even when you're doing something totally simple.
- hackinthebochs 10y agoI feel your pain. Having to manually unwind chained calls just to see which one caused the NPE or whatever is obnoxious and kind of defeats the purpose of this style of coding. IDE's really need support for this.
- village-idiot 10y agoIntellij can drop breakpoints on the Either::map, or in the lambda contained therein. It's really not much different than debugging the if version.
- ajkjk 10y agoThe problem isn't breakpoints so much as stepping through them. You either step through the whole chained method at once, or have to set a breakpoint inside of it. Whereas with loops you can step through loop iterations easily with no additional work.
- village-idiot 10y agoHonestly, this hasn't been a problem. If I get a bad result I merely figure out where I went from a left to a right (and there will only be one spot where that happened), and debug that function only. I've never had to debug the entire chain from top to bottom, ever. But if you're really worried, set a breakpoint in each lambda (trivial in IntelliJ) and use continue instead of step over.
- glibgil 10y agoPlease remove your downvote. You shouldn't use it to disagree with a viable suggestion. Your comment made your argument nicely! Ok, I agree with you. Someone needs to patch Java and other IDEs to allow breakpoints on periods. Double clicking a dot to set a breakpoint sounds like awesome UX. Why hasn't this been done?
- ajkjk 10y ago
- kozhevnikov 10y agoNo sure what you mean exactly, but IntelliJ will show ever-expanding expression list to choose from when setting a breakpoint even when they are chained and on a single line as well as displaying return value from last method call. In my experience debugging such method chain is no different to any other debugging bar variables names.
- deleted 10y ago[deleted]
- ebola1717 10y ago- Don't chain multiple methods in a line. One call per line. IDEs will usually let you set breakpoints per line. - You can still use intermediate variables for readability e.g. instead of nums.filter((n) => n % 2 == 0).map((n) => n + 2).foreach((n) => print(n)) do: nums.filter((n) => n % 2 == 0) .map((n) => n + 2) .foreach((n) => print(n)) or: List<Integer> evens = nums.filter((n) => n % 2 == 0) evens.map((n) => n + 2) .foreach((n) => print(n))
- ajkjk 10y agoGood points. I just feel that this is something IDEs really need to solve, without involving work from me. And it might be a limitation of the JDK debugging system (though I imagine it could be shimmed into IDEs either way). Basically I want to see a "skip to next chained method" button alongside "step" and "step into" and "step over". And maybe another that lets me step to the next iteration of a given chained method as well.
- Tarean 10y agoI think java's problem here is how annoying it is to split these up. For example your example completely split up in java vs haskell: Java IntPredicate even = i -> i % 2 == 0; IntUnaryOperator add2 = i -> i + 2; UnaryOperator<IntStream> process = s -> s.filter(even).map(add2); for (int i in process.apply(numbers)) { System.out.println(String.valueOf(i)); } Haskell: printProcessed = mapM_ print . process where process = map (+2) . filter even
- pekk 10y agoIt's even more concise in APL, assuming you know how to read APL.
- draw_down 10y agoI feel like, if you mean "if" say "if". As opposed to doing a little dance that has the effect of "if true do this, otherwise do that".
- nv-vn 10y agoOne 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.
- bananaboy 10y agoYeah 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.
- EdSharkey 10y agoSomeone 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.
- dtech 10y agoTheoretically there's all kinds of optimizations possible, but I don't think Java/the JVM does a lot of them. The main advantage you'll get is lazyness.
- mike_hearn 10y agoSadly, no. Code written in this style will typically run more slowly, or be equivalent at best. Streams can perform better if you make them parallel and have lots of data as then you can more easily spread out over multiple cores (which is the point of streams), but most cases aren't like that. When you heavily use stream constructions, you're relying on the JVM to: • Synthesise classes for the lambdas. • Inline the map/filter/fold calls and then inline the lambdas too. The JVM doesn't make any guarantees about inlining and may unpredictably bail out. If inlining doesn't happen then profile pollution will kill off some of the other optimisations the JVM does. • Escape analyse any temporary objects created like iterators and then scalar replace them. • Try and do some loop fusion, but I'm not sure to what extent the JIT compilers can do that. This is a long list of complex and often fragile optimisations. If any of them don't get applied then you end up with virtual method calls, objects being created, poor cache utilisation etc. Sure objects that die young are cheap but they aren't entirely free. It's still best to avoid them. The reality is that writing traditional style code is going to be more efficient or at least more reliably efficient than functional style code for the forseeable future. Note that the JVM has a much easier time of it when using Kotlin's support for lambdas and functional programming because the inlining is guaranteed to be done by the Kotlin compiler not the JVM, and that fixes a lot of issues with profile pollution and unpredictable performance drops.
- cjensen 10y agoI 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. Is this a satire?
- maxxxxx 10y agoI am not sure either if this is a joke.
- chc 10y agoThey'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.
- barnabee 10y agoI'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)
- Doradus 10y agoIt 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.
- jhomedall 10y agoI find the map/flatMap version easier to follow, myself. It does takes some time to grow accustomed to, though.
- village-idiot 10y agoHere's how this works. If a function would not return null originally, you continue to return the original type and use Optional::map. If a function could return either T or null, return Optional<T>, and then use Optional::flatMap to join them together. Pretty straightforward.
- amelius 10y agoIsn'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.
- Sharlin 10y agoThis is exactly what the flatMap (aka monadic bind) calls do. flatMap/bind is sometimes called "a programmable semicolon", allowing one to customize what happens when chaining operations.
- trevor-e 10y agoCan you explain the "a programmable semicolon" part? I've tried and tried and can't think of any way that makes sense to me. I use flatMap all the time but still don't understand the expression.
- kccqzy 10y agoIn Haskell a semicolon can optionally be used to separate consecutive monadic binds. It basically means you can change the meaning of chaining flatMaps.
- jasonm23 10y agoPossibly helpful... http://gbacon.blogspot.sg/2009/07/programmable-semicolon-explained.html?m=1 http://gbacon.blogspot.sg/2009/07/programmable-semicolon-exp...
- jstimpfle 10y agoDon't think too hard. You need lots of context: Mostly that a sequence of two statements in a procedural language like C (where these are separated by semicolons) means to "execute the statements and realize their effects one after another". And then maybe you should know Monads as used in Haskell (you could say monads generalize the semicolon to include other meanings such as "can fail", "can have multiple results", "can do IO" etc).
- mabbo 10y agoOptionals 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. Optional<Foo> myValue = gateway.callThatApi(...) return myValue.filter(Utils::isNotTooShabby) .map(this::mapToCoolerType) .map(getDecorator()) .orElseThrow(new TotallyBlewItException()) 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.
- Retric 10y agoWhat's wrong with generics here? ListCondi<ObjType> myValue = gateway.callThatApi(...) foreach(tmp as Condi in myVlue) { if (tmp.worked) { foo(tmp.Generic);} else {bar(tmp.Generic);} } PS: Sorry, have not touched java in like 10 years, but I assume you can have that foreach as a map.
- mabbo 10y agoIn my case above, there's just one object, maybe (or maybe there isn't?). In the case you're presenting, one might consider: Stream<Foo> myVals = gateway.callThatApi(...) return myVals.forEach(val -> getFooOrBar(val).apply(val)) Not quite as nice as Scala would let me do it, but closer, simpler. Streams also let you add filters to the "list" of values, reduce them to a single value with a reducer, or use a nice set of "collectors" that reduce to Java generic collection types. (Collectors.groupBy is so handy). Streams are also lazy, which I think is more good than bad. The thing I tell everyone to do right now, today, is to open their Java code and search for "return null", then replace it with "return Optional.empty()". It'll break your code in lots of places, but things will be better when you fix all those things. Often you'll find lots of places that didn't handle the null possiblity at all!
- jankotek 10y agoOptional types are clumsy when compared to modern alternatives: - 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.
- xg15 10y agoAlso, 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.
- sid-kap 10y agoTrue. 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.
- pkolaczk 10y agoKotlin 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.
- edem 10y agoScala is still a baroque abomination which excels in nothing and the advantages are shadowed by its warts. Kotlin is just Turbo Java.
- 10y ago
- hakcermani 10y agoIn the parse example if the parse fails would we end up with 4 more function calls ? (rather than one if check and a bail out)
- village-idiot 10y agoAny function like map or flatMap that only affects one side of the either is a no-op if the current value is the wrong side. It's a bit like calling map on an Optional::empty, a no-op.
- alexatkeplar 10y agoIt's quite old now, but this is still one of the best tutorials on dealing with failure in FP (here, Scalaz): https://gist.github.com/oxbowlakes/970717 https://gist.github.com/oxbowlakes/970717
- wellpast 10y agoThis is just lipstick. 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: (generate-json (business-logic req)) So this I believe is objectively better: (if (valid? req) (generate-json (business-logic req)) (generate-json (errors req))) 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 10y agoLikewise, using "match", I think the Clojure solution for FizzBuzz is very elegant: (doseq [n (range 1 101)] (println (match [(mod n 3) (mod n 5)] [0 0] "FizzBuzz" [0 _] "Fizz" [_ 0] "Buzz" :else n))) To my mind, this reads much more clearly than if I wrote a bunch of if() statements.
- iainmerrick 10y agoWhat makes it clearer? I think you're just more used to "match", whereas other people are more used to "if". The structure is almost identical. You even have an "else" clause!
- village-idiot 10y ago:else is a convention in Clojure that has truthy keywords. You could replace that with anything that evaluates to true, like :foo, or true, or 1, etc.
- a3n 10y ago> 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 ... It sounds like a ternary operator to me.
- DrJokepu 10y agoIt's not really a ternary operator though, because the value that is tested for existence is only evaluated once, e.g. instead of a != null ? fn(a) : null it's more like (a, fn) => a != null ? fn(a) : null
- jayajay 10y ago> objects don’t magically construct themselves from unstructured data Very out of context, and very off topic, but this is a profoundly deep statement... its veracity is questionable and unknown.
- iopq 10y agoI did FizzBuzz in Rust without using ifs: https://bitbucket.org/iopq/fizzbuzz-in-rust/src/bf4968973d73137f0dfd07205d599bed30a788fa/src/lib.rs?at=master&fileviewer=file-view-default https://bitbucket.org/iopq/fizzbuzz-in-rust/src/bf4968973d73...
- hughes 10y agoThis looks dreadfully complex.
- iopq 10y agoIt's all type signatures. The actual function is seven lines.
- iainmerrick 10y agoThis looks complexly dreadful.
- jkcxn 10y agoHere is something similar I did in JS https://gist.github.com/JakeCoxon/d78fa1debc13e46ae54a https://gist.github.com/JakeCoxon/d78fa1debc13e46ae54a
- zcoyle 10y agoI came up with nearly the same solution in swift: https://github.com/zachcoyle/fizzbuzz-without-booleans/blob/master/fizzbuzz.swift https://github.com/zachcoyle/fizzbuzz-without-booleans/blob/... But I like your solution better
- ronnier 10y agoI'd hate to maintain that.
- kibwen 10y agoOn the contrary, I'm fascinated by the prospect of a job where I'd be paid to abuse type systems to produce useless programs. :)
- markelliot 10y agoConceptually this seems to be a Java approach to Railway Oriented Programming (http://fsharpforfunandprofit.com/rop/ http://fsharpforfunandprofit.com/rop/), which is pretty sweet.
- ndesaulniers 10y agopart two will be titled "Shaders and SIMD without If: scattering and gathering"
- beached_whale 10y agoThis is still possible java.util.Optional<int> opt = some_null_returning_function( );
- hedora 10y agoHey, don't knock it! Maybe I want a null reference to an Optional<int> to mean something different than a non-null reference to an optional that contains a null reference to a boxed Integer. Database theory tells us that having multiple null values is useful ("eh, I don't know" vs "your question has no meaning"). Seriously though, it would be great if they just added a "non null reference" type to the language. C++ has this, and it is useful (even though the compiler doesn't enforce the non-null bit). The thing that always irked me about Optional<T> is that they are synonymous with bare Java references (which also can be null, at least according to the language spec / compiler). It is like a Java version of #define THIS_PROGRAM_IS_WRITTEN_IN_C * to my eyes. To each their own.
- beached_whale 10y agoclang will(at least v4 will) tell you you did a silly thing if it can reason that your a statically binding a reference to nullptr. int & i = (int)0; Not sure how far it can go down the rabbit hole though. This is really where value types/regular types are really nice. I think it would have been better for Java to allow the flatmap like stuff for any reference instead of creating an optional. But I am not a fan of either for when there is no value due to error conditions. There are optional values and there are errors that prevent the fulfillment of the contract. So either throw so something can be done or return a class that can either be an error or the value. But returning null does not allow the caller to act on it. You are so right though about non-null references. I would go further and say nullable should not have been the default but an added keyword. That is the trait of a c++ reference I like. Just use it and don't check for null. With that Optional can have some differentiation and is meaningful for situations where an error hasn't occurred.
- ebola1717 10y agoEh, in that case you should roll your own container type appropriate to the business logic, e.g. DBWriteResult<T> (or return Optional<Optional<int>>, but probably not that)
- beached_whale 10y agoAs 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.
- village-idiot 10y agoWhy is map and filter nice on a list? Because the individual functions passed in have no idea they're in the middle of a map or filter. Ditto with mapping or filtering on Optional or Either. It composes better.
- kbuchanan 10y agoI 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?
- virmundi 10y agoTyping. 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.
- raspasov 10y agoCheckout clojure.spec - you might find it very useful. The problem with types is that they are only a static/at rest description of your data. For example, it's a String or it's a Date. But does that String contain @ character (checking for email)? Is this Date in the future or in the past (validating a credit card form)? Types say nothing about that. I'm not saying that types have zero utility, but in the vast majority of my use cases compile-time type checking doesn't go very far.
- tigershark 10y agoYou can always create an Email type or a CreditCartExpiry type that validate in the constructor if the string contains @ or if the date is in the future or in the past. In this way you are guaranteed that you cannot pass around an invalid email address or an invalid credit card. Obviously in Java nothing guarantees that you won't forget to handle the failed state, while in languages with exhaustive pattern matching is pretty much granted if you write idiomatic code.
- virmundi 10y agoI don't like spec. I liked the other one (by Mars I think that actually checks nested objects). Perhaps I'm just stuck in my ways, but I like to be able to see in the code I'm looking at what attributes or actions are applicable to this thing. With Clojure I lose all of that. What is in this map? Well, I better print it out to know. Clojure's dynamic typing, and any dynamic language to me, is only useful if you're never more that 2 stack frames away from your data's source. After that it's a lot of documentation to make sure you don't cock it up.
- rco8786 10y agoSo, Scala.
- Sharlin 10y agoWithout the huge complexity of Scala and faster compile times. Java 8 is actually a surprisingly pleasant language to write.
- slantedview 10y agoThere 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. [1]: http://www.javaslang.io/ http://www.javaslang.io/
- oconnor663 10y agoThe Either type there is very similar to Rust's Result type.
- village-idiot 10y agoNeither are new concepts, both are probably lifted from Haskell which probably got it somewhere else prior.
- nurettin 10y agoJava 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.
- fulafel 10y agoInventing 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.
- general_ai 10y ago2 years from now: "we got rid of all the functional bullshit and we're now using ifs and for loops all over the place; reminds us of our fixie bicycles and other aspects of our hipster lifestyle". Over the past two decades I've internalized the value of writing code that's very easy to understand. Otherwise 6 months later I can't figure things out myself. This Java style reminds me very much of Scala which seemed like a decent language until I saw how people actually use it in practice. Noped right out of that in a hurry.
- eveningcoffee 10y agoI think what you are describing is a replacement of widely known constructs (if, for etc) with an unknown API. If this API is not understood then the code feels convoluted. Therefore such projects have to contain a document that will explain the most common usage of the API to the newcomer. I think that this would remove most of the confusion. Naturally it would be good if we had one most widely standardized API that most of the people are familiar with (like they are with if and for).
- cel1ne 10y agoReading an API's documentation doesn't mean that the mental overhead when using it is gone. You still have to think about how the pieces fit together, how to get from generic examples to your specific case etc. I can dream up APIs which will keep confusing you, no matter how long you use them (hello Android SDK!). Or which have difficult to memorize syntax (hello Bash!).
- AnonymousPlanet 10y agoI think this has to do with implicit behaviour vs. explicit statements. Using Optionals introduces an implicit layer that is much "thicker" than even the most tricky for syntax out there. There is a strong case for keeping all involved parts as simple as possible. And this assumes there are no implicit caveats and exceptions in the overall behaviour of the type, or that any one decides to subtly move the goal posts behind the scenes five years down the road.
- ahoka 10y ago
- edem 10y agoSo 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?
- deleted 10y ago[deleted]
- village-idiot 10y agoTry to up your reading comprehension before you question the competency of others.
- edem 10y agoTry to down with the sickness your brain has before commenting something utterly useless and calling names. I was talking about the lack of competent workforce.
- etaty 10y agoReally Hacker news?! 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.
- Inufu 10y agotl;dr: Don't use Either<L, R> for error handling, it's too general. Use StatusOr<T>. More details: http://www.furidamu.org/blog/2017/01/28/error-handling-with-statusor/ http://www.furidamu.org/blog/2017/01/28/error-handling-with-...
- SeriousM 10y agoThe fact that you don't write "if" doesn't mean your aren't using it. Dann clickbait.
- arximboldi 10y agoThe potential for JSON parsing to fail is encoded in its type, not in the potential for a variable to null, or false, or for an exception to have been thrown. You’re leaning on the compiler to tell you if you’ve handled the failure cases properly, as the code won’t compile otherwise. Now instead of testing for runtime exceptions you only test to make sure that your business logic is correct. Last time I did Java (it like 7 years ago) the compiler did enforce exceptions types as part of the signature. Has this changed in between? Otherwise this does not seem like a valid argument to me. The OP does discuss checked expcetions a little bit: Checked exceptions guarantee that someone will deal with the issue, but they are extremely annoying, and might result in disparate and different exception handlers all over the place. They don't explain why exceptions are annoying (because the compiler checks them, just like optional?) and technically there should be exactly the same number of try-catch handlers as match calls in equivalent Optional<> code... It seems to me that his arguments are mostly based on aesthetics. Something the author half-acknowledges by starting their discussion with "Well, first off I think it’s beautiful." There are some actual problems with exceptions though: 1. It may be hard to tell from looking at the code which particular calls inside a function produce which particular kinds of exceptions. 2. This is particularly problematic with stateful code, as to ensure exception-save stateful transactions. 3. They tend to be more expensive for the exceptional codepath -- on the other hand, they are faster than Optional for the non-exceptional path! In my experience points 2 and 3 are the most important. Since in Java many things may throw, one has two think about exception safety anyways most of the time. This is also a good argument maybe, to just avoid statefulnes instead. Point 3 is very important. Maybe exceptions should be relegated to truly exceptional situations, and not be used as a replacement for an if. Optional<>/Either<> is excellent in this sitiation. Still in some languages do use exceptions for this and familiarity might be something to consider there (I'm thinking of idiomatic Python signals iteration end or key presence in maps, but Python also has very different performance characteristics as Java and does not have static types for the most part anyways...). As much as I actually love FP, and I am also trying to bring more FP to other languages (C++), I don't believe in fighting the language for the sake of it. Having a nuanced conversation about the "why" and a the "when" is important. Specially when bringing these techniques to communities that are not used to them and, at the end of the day, already have methods that "Just Work TM". Otherwise, you end up having reactions like this: https://news.ycombinator.com/item?id=13505620 https://news.ycombinator.com/item?id=13505620