Hacker News new | past | comments | ask | show | jobs | submit login
Java Without If (ashtonkemerling.com)
352 points by tretiy3 on Jan 27, 2017 | hide | past | favorite | 208 comments



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.

  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.


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 point of a static type system is to have the language help you out as much as possible to write type-error free code. It can't guarantee that you haven't messed up, but it's supposed to help.

Null ruins that. It's a known source of runtime exceptions, and it is not a compilation error to return null or forget the null-check. It is a compilation error to pretend that an Optional<T> is just a T. The Java compiler is not smart enough to prevent you from doing something silly like calling get() on Optional<T> without checking isPresent() first, but at least it gives you something.

Also Optional has some really nice creature comforts. It gives you the ability to map to compose an Optional<T> with functions that couldn't care less about the Optionality, and it lets you use flatMap to chain successive calls that might fail together, avoiding an arbitrary number of null checks (that you might forget!) in your code.

Frankly arguing against Optional<T> is like arguing against map on lists/streams and saying that for is good enough for you. You can do it, but I think you'd be nuts to work against yourself like that.


Kotlin does it more efficiently. But you wouldn't want it to be an error to use get without calling ifPresent. Sometimes you know that the type system is wrong and that in fact, although this thing is optional, it will in your case always be there. So being able to rapidly convert optionality into an error (we might call it a NullPointerException) is important. Kotlin has the !! operator for that.


Simple answer: never return null.

If something is wrong, throw an exception. If it's the kind of error that must be handled, throw a checked exception. If there's no value to return, return Optional.empty.

The code receiving that Optional<Foo> is now free to do what the author is suggesting: map, filter, all without worrying if the value is actually there or not. The code is cleaner, easier to read, etc.

But no one should ever have to check for null anymore.


But why returning Optional.empty better than returning null?


It gives you a type/interface level guarantee.

e.g. you know

    public Optional<File> loadFile(String filename);
can fail to return a file, while

    public Int sum(Int a, Int b);
can't fail to return an Int.

Obviously, because nulls exist in java, this guarantee isn't provable on compilation the way it is in e.g. Scala, but if your entire team writes code this way, at least you can rely on that for internal libraries. Like generics, this is one of those ideas that's ok in java 1.X, but doesn't shine as much until a backwards incompatible java 2.X


Nullable types provide Optional already. I find the use of a custom wrapper type to be a massive wart, and Optional is a common enough thing that it's worth adding a bit of syntax for it.

Which is why I like Kotlin's approach. Same guarantees, one letter instead of ten. If you're writing Java, though, I can see why Optional has some use.


Nulls exist in Scala. You can return null for an Option[T].

Can't in Kotlin, though.


When interoperating with other libraries on the JVM you very easily can, which is a very common use case.


You're right. Kotlin provides the null-or-throw operator (!!) to convert a Derp? into a Derp, though.


Often when you're dealing with a value that may be null, you might want to do a series of transformations to it that may change its value or type, and may even return null themselves. Optional lets you chain these actions together without concerning yourself at every single step whether or not the value is there until the very end.

Let's take this back to map/filter on lists. Remember the bad old days when you didn't have map and filter on lists? If you had some code that iterated heavily and then you needed to add another step you often ended up refactoring a lot of manual for code that explicitly handled iteration logic. Now with streams you can just chain another call onto the same stream and as long as the types match up you're ready to go test it. Optional gives you the same thing, but instead of working with lists it handles values that might be missing.


Because null is overloaded with meaning - it could be an IO error leading to an empty result, or IO could have produced an actually empty result, or ...

Yes, you could argue that you will only use null for an actually empty result. But two things: 1) You won't keep to it because the compiler won't force you to do so, and 2) potential contributors will not comply. When using optionals/either/enums, compiler will not allow a wildcard result (such as null).

.empty is just more semantically specific, and if you read a method's API for the first time, semantics helps you grasp it a lot faster. That's the reason I'm using enums in Swift all over the place now. Compared to the nilling out of Objective-C, I can be way more confident that method results really are what they claim they are.


The biggest reason- I very rarely need to check if my optional is empty or not. I can write optional-chaining code like in the article above and it implicitly handles both cases.

The code I've written using optionals instead is much shorter, much clearer in its intent, and generally has fewer bugs.

That's anecdotal experience, not proofs. All I can really recommend is that you try it with an open mind.


"If something is wrong, throw an exception."

That is sometimes just not an option performance wise yet.

"If it's the kind of error that must be handled, throw a checked exception. If there's no value to return, return Optional.empty."

Again, why is Optional.empty() better then null? What makes it better? What do you get from throwing an exception? What is the benifit. You can't just say "my way is better" when we have years and years of Java development flying contrary to your statement. What has changed that makes null a non-viable practice?

"The code receiving that Optional<Foo> is now free to do what the author is suggesting: map, filter, all without worrying if the value is actually there or not. The code is cleaner, easier to read, etc."

The code being cleaner is a subjective, or at least extremely difficult to prove, statment. For instance

    List<Integer> nums = getNumbers();
    if (nums == null) {
        S.o.p("Failed to load numbers");
        return;
    }
    for (Integer i : nums)
        System.out.println(i);
Is far better then

    Optional<List<Integers>> nums = getNumbers();
    if (!nums.isPresent()) {
        S.o.p("Failed to load numbers");
        return;
    }
    nums.forEach(S.o::p);
Or even better yet

    for (int i : nums)
        if (i < 10)
            S.o.p(i);
VS

    nums.filter((n) -> n < 10).forEach(S.o::p);
I don't think that's more readable. It think that's more compressed. HEre's another example. Suppose we have a magical language that I'm sure you'll pick up. It's a very compressed (or as you'd say expressive) language. This is that same code written in it

    pa(i i nums < 10)

Which expands to "print all the ints `i` in nums that are less then 10" in english. That's far less readable. It is more compressed. I don't think compression is the goal of a language as much as it is a goal of a zip program. We're supposed to be writing software, not liturature to submit to the shortest story fully told contest. Readability is a function of correct verbosity.

In my opinion. Just compressing your logic doesn't make it more readable. I think some verbosity is needed to preserve simplicity.

"But no one should ever have to check for null anymore. "

I mean that just doesn't make sense. If you're suggesting that there are some times the state of a program should never contain a null value is just ridiculous. Some things are correctly modled by null and some things are also too performance dependant to not use null.

I think some things benifit from using Optional<> but the case doesn't exist to completely remove null. Even just by the creation of a new container object wrapping your already expensive return object there exists a case for null to exist.

Just saying "Don't do it it's bad" is not proof. Saying "the code is cleaner, easier to read, etc" is not proof or even an example of a design that is simpler to pickup and get going with. You'd have to write some code with the Java/OOP paradigms and write a version (that is feature complete) with the FP paradigms and see which is easier to understand for a new programmer. I'd be hard pressed to belive that the FP implementation would be simpler. Maybe to you and me but no to someone without the domain specific knowladge required to understand what's going on. Even when I use map, zip, and list comprehensions in my python code it scares off some of my coworkers.


In practice, your first example would be more likely to look like:

    public List<Integer> add2(File file) {
        List<Integers> nums = file.getNumbers();
        if(nums != null) {
            List<Integer> resultList = List<>();
            for ( i : nums) {
                resultList.append(i + 2);
            }
            return resultList;
        } else {
            return null;
        }
    }
Compare that to:

    public Optional<List<Integer>> add2(File file) {
        Optional<List<Integers>> numsOpt = file.getNumbers();
        return numsOpt.map((nums) =>
            nums.map((i) => i + 2);
        );
    }
I find it hard to argue that the latter is worse.


    public List<Integer> add2(File file) {
      List<Integer> resultList = new ArrayList<>();
      for (int i : ListUtils.emptyIfNull(file.getNumbers())) {
        resultList.append(i + 2);
      }
      return resultList;
    }


And now you need to use a non-language builtin util everywhere. And this one only works when working with arrays or array like objects...

It's amazing how far people will go to defend a bad practice, than even its inventor called a 'billion dollar mistake'.


This returns an empty list when file fails to read, which is not the expected behavior.

That's a case that comes up a lot! DB read failed vs DB read 0 items for example.


    public List<Integer> add2(File file) {
        List<Integers> nums = file.getNumbers(); // Show where the data comes from

        if(nums == null) { // Show the invariant provided for the rest of the code 
            return Collections.<Integer>emptyList(); // Show the default return value if there has been an error
        }

        List<Integer> resultList = List<>(); // Apply your logic
        for ( i : nums) {
            resultList.append(i + 2);
        }
        return resultList; // Return your list
    }
Then you document that behavior in a javadoc. Throw in @Nullable where applicable and move on. Or you can do this

    public List<Integer> add2(File file) {
        // Show data source
        List<Integers> nums = file.getNumbers();

        // Show case where data is supplimented
        if(nums == null) 
            nums = Collections.<Integer>emptyList(); // Show supplimnetal

        // Show transformation
        List<Integer> resultList = List<>();
        for ( i : nums) {
            resultList.append(i + 2);
        }
        return resultList;
    }
This is important to unify because nums won't always be null. We also won't always be supplimenting with an empty set. I don't see what value is recived from compressing that all into a single line. I don't see that as being more readable. I do see this very simple, step by step, explination of what's happening as being dead simple that no one can misunderstand.


> Again, why is Optional.empty() better then null?

Because with Optional the type tells the caller that it might not return a value. With null, you have no idea if you need to check for null or not.

Being able to chain maps, filters, etc. is convenient as well, but I'd argue that's just a side benefit.

Oh, one other thing Optional can do that null can't: nest. For example, if you call get() on a Map, and it returns null, you can't tell if the key wasn't in the map, or if it's value was null. If get() returned an Optional, then for a Map<String,Optional<String>>, get() would return Optional<Optional<String>>.


Your very first method has a return buried in the middle of your code. That is a gigantic red flag.

Look, what you're trying to do is simple.

  getNumbers.fold(println("Failed to load numbers"))(nums => nums.foreach(println))
That's the Scala version, but I'm sure there's a Java version of the same code. This code assumes that getNumbers returns Option[List[Int]], which your compiler can guarantee. If you want something that's a little bit more readable, try this:

  getNumbers match {
    case Some(nums) => nums.foreach(println)
    case None => println("Failed to load numbers")
  }
No null check, no loops, just very simple easy-to-read bulletproof code. This code cannot generate a NPE at all, ever.


The first option ins completely unreadable to me. I cannot imagine that ever scaling well.

The second option, as I have stated many times in this thread, is far preferable to null. Java does not support it but when it does I will like it.

"* No null check, no loops, just very simple easy-to-read bulletproof code. This code cannot generate a NPE at all, ever.*"

The NPE isn't the illness, it's the symptom. It's the symptom of a far larger problem in your program: unhandled or otherwise unexpected state. That's why I like the match example. It forces you to expect all of the states of your program. The first example does neither. It hides flow and operation in a single (ever-expanding) line. Far from ideal in any case in my book.

As soon as Match is supported in Java I'll change my opinion. Until then I'm stuck. You get nothing better from the Java version in terms of readability.


Fold is incredibly important. You should learn it. https://en.wikipedia.org/wiki/Fold_(higher-order_function)

In Scala, the debate between using fold on an Option or using map and getOrElse is almost as old as the language. The method signature for this is:

  def fold[B](ifEmpty: => B)(f: (A) => B): B
That is to say, fold always returns type B, and takes two arguments. The first takes a function returning type B if the option is "empty" (None) and the second takes a function that is called with the contents of the Some. As you can see, it's syntactic sugar for map and getOrElse.

  def map[B](f: (A) => B): B

  def getOrElse[B](f: => B): B


If it's syntactic sugar for map and getOrElse what is really gained? If the definition of fold is mapAndGetOrElse why add an additional concept to burden programmers minds with? GetOrElse is independently useful and everyone needs to know map anyway. Using fold just seems like a lost opportunity to teach someone getOrElse with no real benefit.

You make the Understanding This Codebase 101 curriculum some percent longer without making your programmers any better.

If it's terseness, I don't really think one symbol is any verbosity benefit over two. Same order of magnitude, same cost. It's a rounding error in brevity. People way overvalue terseness.

And the cost of people missing chances to learn getOrElse has got to be massive.


I haven't used Scala for years, but fold[0] is one of the functional programming building blocks. It's applicable across languages and data types.

It looks like syntactic sugar here, but it's really not. It's just a function that is more commonly used on collections with more than one item.

[0] also reduce, inject, aggregate, and other synonyms, because naming is hard


map and getOrElse are syntactic sugar for fold, not the other way around.


>The first option ins completely unreadable to me. I cannot imagine that ever scaling well.

It's actually trivial, and has been used in all kinds of languages, and scaled just fine, since the 60s.


It's quite simple: null is not a valid value for any type. It completely breaks the entire point of a type system. Null isn't an Integer, nor a String.

Simple example that proves my point: You can't call .length on null, but you can for every string. Therefore null isn't a string. So why in hell should you be able to write code that states (falsely) that null is a string? You can't say an int is a string.

Removing null from a language adds a tremendous amount of safety.


> "If something is wrong, throw an exception."

> That is sometimes just not an option performance wise yet.

Why? I haven't heard about performance issues of exceptions since 90-ties and C++ problems.

And if you consider that you can throw exceptions instead of error codes (which null is an example of) why would a list of numbers be ever null?

It should be an empty list, not null.

Generally you don't add Optionals to Collections/Iterables/Maps because they already have a notion of empty.

So your example becomes:

    List<Integer> nums = getNumbers();

    for (Integer i : nums)
        System.out.println(i);
And if getNumbers has an error, it should throw an exeption.


Or use the null object pattern. Then you can write code that "just works"; kind of a higher level version of a nop.


The Null Object pattern is The Correct Answer (for Java like languages).

Java's Optional is a bad idea implemented badly made even more bad by bad usage.


Option is a takeoff of the Null Object pattern.

Option(null).map(str => str + "!").foreach(str => println(str)) is a noop.


Yeah, the Null Object Pattern is just optionals except you have to re-implement it each you need optional semantics.


No. They're opposites.

"...an object which implements the expected interface, but whose method body is empty. "

https://en.wikipedia.org/wiki/Null_Object_pattern


you shouldn't really be running `optional.isPresent()`, but rather, use a map operation, or a flatMap operation, all the way until you need to interface an outside api (in which case, you use a `getOrElse(defaultValue)`).

You're right about code that branches based on `isPresent()` is semantically the same as null checks - and if you only did that and didn't use any functional paradigm like mapping on the types, you don't really get any real benefit!


Optional types shine the best when you can chain them:

BufferedImage avatar = users.getUser(userName) .flatMap(User::getAvatarUri) .flatMap(ImgIO::read) .orElse(anonymousAvatar);

I made this up, but I hope you see the point. Optional is just a fancy null pointer if you don't use it in a functional way. :-)


I think the most benefit of wrapping the result in an optional is so you can use compiler to enforce exhaustive pattern matching[1].

   Optional<Integer> num = getSomeRiskyNumber();
   match num {
     Some(x) => ... ;
     None => return error or whatever;
   }
The compiler will verify you at least pretended to look at the None result instead of just ignoring it (but you could still swallow the none).

1: does java even have this?


To my knowladge the Java library does not although it is fully capable of supporting this. I guess this is just an example of poor implementation. It could have been easily implemented like this

   Optional<X> x = ...;
   switch (status(x)) {
       case SOME: return x.get() * 10;
       case NONE: .... send error ...
   }
Again I really like the matching functionality but as it stands, since it's not included, it's not worth much without it in my opinion.

With match, that cleans up a LOT of code and if the compiler can guarantee no speed or memory hits for doing this it becomes very valuable very quick.

It's one of the reasons I wanted to learn Rust.


Incidentally, Scala discourages using pattern matching in cases like this (pun not intended), preferring the use of combinators (map/flatMap/etc).


The closest equivalent would be something like:

  Optional<Integer> num = getSomeRiskyNumber();
  return num.map(x -> ...).orElseGet(return error or whatever);
Which is just as type-safe. There are a number of variants, including flatMap for when the map operation itself returns an Optional (the monadic bind operator) and a number of options for how to deal with the empty case.


Optional forces the null check (at the cost of a pointer dereference :( but that's Java for you). That's the difference.

It also provides some nice combinator syntax for handling that null beyond .isPresent, but if you were to just get .isPresent it would be a win.

Documentation is optional (hah) and therefor shouldn't be relied on.

Optional as a return type means there's no need to check for documentation, there's no need for @Nullable, and you get some pretty syntax to boot.


A few things that need responding to. First, optional is the same as a maybe. They're different names for the same thing.

If you want to return a status instead of nothing, you use an Either type (as mentioned in the post). In an Either, you populate left or right, but not both. You can let left be your status and right be your template type. You could codify this as StatusOr<T>.

In a StatusOr land, you return if there's an error; otherwise you continue the computation. This gives a few advantages, but one is that you control the flow of control. (This is also done decently with exceptions in Java, iirc)

However, all of these - maybes, either - they make your functions composable.

There's a good talk... Railroad oriented programming... that dives into this.


In Scala, you're encouraged to write more bulletproof code by avoiding exceptions at all time, simply because it's easier to realize that (in your case) getSomeRiskyNumber returns an option instead of an integer.

  getSomeRiskyNumber map {
    case Some(i) => Map("risk" -> i)
    case None => Map("error" -> "Error retrieving number.")
  } map { response =>
    Json.toJson(response) 
  }
Reading this code you know you're always going to generate a json response to the user, and depending upon what getSomeRiskyNumber() returns, the json response will either look like { "risk": 23 } or { "error": "Error retrieving number." }. No exceptions needed.


This is something that can be paralleled easily with @Nullable. Your IDE will even tell you, before compiling, that you should expect a null case.

Also, currently, Java doesn't have a match {} expression that works with Some() and None. I belive that would completely change my opinion and the name of the game.


The match is just my personal preference to deal with option types, because I personally think it's cleaner to read. This reduces the number of flatMap chains in my code, but again, personal preference.

  Option(JavaClassFactory.staticMethodThatCanReturnNull()).map(i => i + 1).getOrElse(0)
This is a very readable function, imo, and is how 99% of Scala programmers deal with legacy Java libraries. Scala even has the Try() object, where you can do something like this:

  Try(OldJavaClass.methodThatThrowsExceptions()).toOption.map(i => i + 1).getOrElse(0)
It makes creating service-layer code so much simpler because you aren't caught in that rut of "well now I have to deal with this exception at the controller level". You can head off all that garbage very quickly. I suspect that a lot of Java Optional coders will start acting like this.


But then you will never know in your code if there was an error. What if you want to send an other HTTP response in case of an error?

With an exception you can still generate a json response, but also handle errors in the way you like.


>Why is Optional<> better then throwing an exception or returning a null?

https://www.infoq.com/presentations/Null-References-The-Bill...


> To just return 1 value you've allocated at least one object (Optional)

May be nitpicking, but in your other example, you've allocated at least one object (Integer) to just return 1 value, which means that supertight performance is not important in getSomeReskyNumber


First of all, the use of isPresent is a red flag that optionals aren't used right.

You'd typically use a sequence of map, flatMap and orElse calls. For example, this is some controller code I'm currently shipping:

    return Drawing.getForLocation(id)
        .map(d -> ok(Json.toJson(d)))
        .orElse(notFound("No drawing for location " + id));
That construct is more readable to me than an if(drawing != null) check. The cognitive load issue is a bit of a red herring. I'm used to optionals now and they're no more of a cognitive load than an if construct. But that's eye of the beholder stuff, so let's park the cognitive load issue for now.

Getting down to the meat of your argument, why is optional better than an if not null check or an exception?

I think anyone can see why it's better than throwing an exception. Optional makes it obvious to the caller that (a) there can be no response, and (b) what happens exactly when there's no response. With exceptions, either they're unchecked, in which case you don't know what exception to catch without peeking, and it's really easy to forget to add a try/catch handler at all; or it's checked, but even in that case the handling code is uglier than an equivalent optional construct.

The more interesting question is why it is better than deliberately returning null. Again, it's about signaling. A library author can signal to the caller that they should expect not to get a value sometimes. Yes, it's possible to return null from a method that returns an optional, and this will cause an NPE, but in that case the error is the library's, not the callers. In making API's, you want to encourage correct use, and optionals help do that.

Optionals in java suffer from the same problem as most API's in java do though. They're uglier than they need to be, and some common use patterns are awkward. For example, in my spark code in scala I'll use constructs like this one to convert a stream between types where the conversion might fail for some elements (and I don't care about the ones where it does):

    val someIntermediate = someStream.flatMap(convertMaybe)
That works because Option can be handed to flatMap directly.

But in Java streams, you can't do that, and you end up with constructs like this:

    someStream.map(convertMaybe).flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())
Which is insane. In Java 9 they're kind of sort of fixing this and it's going to be

    someStream.map(convertMaybe).flatMap(Optional::stream)
Which is still ugly though.

Any way, I think optional has its uses in Java. It's definitely something that helps when building out robust API's. But where optionals would be most valuable, the combination with streams and futures, is where they're painful to work with. That's par for the course in Java though. Oracle's not actually all that good at designing API's. Usually the first attempt at something turns out so broken it has to be replaced wholesale later (like java.util.Date), or it's so ugly it feels bad to use it (CompletionStage? seriously?). But, to each their own, I'm sure my attempt at it would be even worse, and despite the warts I still like Java 8 overall.


What'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.


In 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!


This 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.


Likewise, 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.


What 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!


: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.


But don't you see what I'm saying? I assume the order is important, and if the else: (or whatever else you decide to use) were first it would match everything and prevent the other cases from being used. So it's exactly equivalent to an if/elif/else chain. The structure is the same.


Things can be semantically equivalent but more elegant, easier to read, and harder to make mistakes.

I think most function programmers are actually very familiar with the patters found in Java. It's often one of the reasons they fell in love with more functional styles of programming.


Nice because it is readable but I generally dislike clever/elegant solutions for fizzbuzz. Here is my own clever haskell solution:

  module Raindrops (convert) where
  import Control.Monad (liftM2)
  import Data.Foldable(fold)
  import Data.Maybe (fromMaybe)

  convert = build rules
  build = liftM2 fromMaybe show . fold
  rules = uncurry rule <$> [(3, "Pling"), (5, "Plang"), (7, "Plong")]
    where rule i s j = if j `mod` i == 0 then Just s else Nothing

Short explanation: the fold function combines lists of monoids. In this case it combines three seperate monoids and does `[Int -> Maybe String] -> Int -> Maybe String`. It takes a list of functions that might create a string from a number, gives them all the same input and concats all strings that were returned. If none were returned the result stays Nothing and is then replaced by the string representation of the input via fromMaybe.

I like this solution because it shows how powerful it is to abstract over these concepts but also that trying to be too clever quickly ends in impossible to follow complexities.


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.


While it's "neat" to encode a lot of stuff into types, it also makes code comparatively very difficult to understand for almost everyone. A big part of this is that documenting type systems is even harder and the tooling is even worse than documenting regular code, at least in mainstream languages (like C++; some called me a template guru years back), and of course that many developers are simply not very experienced with complex type systems. For that reason most developers stay well within the bounds of their languages' type system; it arguably gives you code that may be more "plain" and less "smart", but also code that far more developers will be able to reason about efficiently.


Feature rich type systems are really great if you have a good understanding of your types. Often, you don't. One of the best parts of software is it's malleability. Pushing business logic up into types makes it less malleable.


Types don't force you to have a concrete understanding of the problem (although they can help think about it). They help you make concrete decisions about your solution. This can save time when changing things often.


Actually, the code "branches" four times.

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.

Is this a satire?


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.


Here'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.


I am not sure either if this is a joke.


IMO it's the eta-reductions that are terrible for readability. I think the following is fine:

    Optional<String> x = Helper.functionOne()
                           .map((descriptiveName) => Helper.functionTwo(descriptiveName))
                           .flatMap((descriptiveName) => Helper.functionThree(descriptiveName));


I find the map/flatMap version easier to follow, myself. It does takes some time to grow accustomed to, though.


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.


Checkout 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.


You 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.


Type checking is only part of it, the other part is encapsulation of both data and behavior together.

Basically if you have only a few different shapes of data, Clojure is great. But the moment that you have lots of different data types that are similar but require different behavior, Clojure starts to struggle.

Let's make a trivial example. Imagine that there are 4 different ways to identify an object in your system that are recognized industry wide, you cannot eliminate or choose not to support them. These ways are by the keys :foo, :bar, :baz, and :quux. They are not interchangeable, each is unique, and each one requires a different combination of database or API access to use correctly.

In Java you would probably create an empty interface for ObjectIdentifier with tiny-type classes at Foo, Bar, Baz, and Quux. Methods that only need an identifier of any type could accept/return ObjectIdentifier, but at the integration point you could specialize on each type to hit the correct APIs/Database Tables/Etc.

In Clojure you've got if cases, multimethods, and protocols. Frankly we've had big issues with multimethods and protocols. Without enumerating every single issue we've had, they tend to increase the WTFs per day of the system, and they easily confuse both the programmer and their tooling.

So you end up with if cases, probably related to key/values in a map somewhere. But now you've got two really big problems:

1) Methods that just need an arbitrary identifier of some type end up having to pass the whole map, or use select-keys all over the place, requiring that you modify every point that touches this map when you need to add/remove an identifier.

2) You end up with an implicit ordering between these identifiers. What do you do when your map contains {:foo "foo identifier" :bar "bar identifier"}? In Java you'd have a Foo instance or a Bar instance, never both. But in Clojure you need to pick which one to use which implicitly ranks these choices, or throw an exception or similar. Now obviously if you're deserializing json you will need to make that choice in any language, but in a static one you make that choice exactly once and you're provided a measure of safety by the compiler. In Clojure those decision points leak all over your system.

Ultimately there are ways out of this issue in Clojure, and we've actually tried several. But frankly a lot of the solutions start to feel like a half-baked implementation of plain old Java interfaces and objects.


I 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.


Well, with sufficiently good spec definitions you can know (and generate examples of) exactly what is expected to be "in this map".

Oh, spec also serves as documentation!

Maybe you'll like more now. :)


Optional 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.


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.


Scala is still a baroque abomination which excels in nothing and the advantages are shadowed by its warts. Kotlin is just Turbo Java.


Why so angry? I pointed to one obvious limitation of Kotlin null handling compared to Scala and you're attacking the whole language. Scala and Kotlin both have their place and they are not direct competitors at all, because they have different goals and address different groups of programmers, despite Kotlin drawing actually a lot from Scala. It is actually quite funny, when features in Kotlin are presented as making the language more productive, yet the very same features in Scala are presented as an example of "Scala being baroque abomination".


What makes you think I'm angry? I thought that scala was an abomination long before Kotlin came to existence. And I usually compare it to clojure which not only tries but succeeds at being a functional language and on the expressivity scale nothing gets near a lisp dialect. Your comment added no value and you obviously despise Kotlin. From what I see in the city I live: 80% of scala projects fail to deliver and never get to production. The older projects which survive suffer from the slow compiler and the binary incompatibility. Not to mention the arbitrary use of all the tacked-on features by scala devs which makes streamlining code style frustrating from both sides.


"What makes you think I'm angry?"

The wording you used and the fact that your post was a completely off-topic opinion, just to attack the language you don't like. We're discussing various null-safety approaches here, not opinions about which language is better. That Kotlin can't treat optional types as any other monad (e.g. Either or Try monads) unlike Haskell, Scala and probably half a dozen other languages is a fact, not opinion. Also baking optional types into the syntax and the type system makes them language more complex and this is also a fact.

> 80% of scala projects fail to deliver [citation needed]


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.


Nothing you just said is true. Optional doesn't add complexity, it makes complexity explicit. The rest is just Java implementing it poorly.


And because it is implemented poorly, it adds complexity. Even Scala, which had Option from start, has three ways to return value from Map (Option, nullable return value, and throw an exception if not found). Kotlin has only single way.


> (Option, nullable return value, and throw an exception if not found)

Which Map are you talking about? I don't see the method that returns nulls for missing values.

> Kotlin has only single way.

... with the nice side-effect of being unable to tell whether the key didn't exist or if it was null. That's the single worst approach one can take.


Java maps return null, sometimes I have to use those in Scala.

Most Java Collections does not allow null keys and null values.


So you blame Scala both for Java's mistakes as well as addressing them, while Kotlin, which doubles down on Java's poor decisions does everything right? This makes literally no sense at all.

Also, only a few of the newer concurrent Java collections disallow nulls. All the classes commonly used allow null, as well as the collection interfaces themself.

Java had the chance to address this with Optional, but they got the implementation of the null handling completely wrong. Now they are stuck with it, which caused an obscene amount of complexity in the new generics spec to work around it.


Is 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.


No 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.


- 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))


I 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


It's even more concise in APL, assuming you know how to read APL.


Good 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.


Set a breakpoint in first expression in the function that is being mapped unto?


That'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.


I 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.


Intellij can drop breakpoints on the Either::map, or in the lambda contained therein. It's really not much different than debugging the if version.


The 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.


Honestly, 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.


Please 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?


I didn't downvote you.

Breakpoints on periods and the ability to step through them. Honestly if I have to use more than one breakpoint to find a problem because stepping doesn't work, I'm unhappy.


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.


This 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.


Can 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.


I understand monads and don't think it's a helpful metaphor, just kind of cute once you already understand what it's trying to explain.


At least I understand the metaphor as "being able to inject logic, depending on some context, between each operation in a sequence of operations". Alternatively, it could be thought as programmable function composition. So, for instance with the Either monad, instead of some code like

    var x = foo();
    var y = bar(x); 
    var z = baz(y);
you'd have

    var x = foo(); // returns an Either
    var y = x.flatMap(bar);
    var z = y.flatMap(baz);
where some extra computation happens in the flatMap calls that actually determines whether and/or how the function is called and what the actual return value is. In Either's case, if any of the chained operations returns an error, the subsequent operations are not executed and the error is propagated to the end of the sequence instead.


Don'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).



In Haskell a semicolon can optionally be used to separate consecutive monadic binds. It basically means you can change the meaning of chaining flatMaps.


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.


Why 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.


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.


Sadly, 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.


Theoretically 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.



I'm more of a fan of the zip/cycle approach:

https://play.rust-lang.org/?gist=3fb51314d7df9249c9f774dde96...


My approach works with arbitrary conditions, that's why it takes closures as arguments. So it's a little bit of a different requirement, I wanted something as general as possible.



I like this approach. The only thing I would criticize is that it uses '' as a substitute for None.

So something like

    fizzes, buzzes, empties = str_cycle('Fizz', 3), str_cycle('Buzz', 5), str_cycle('', 7)
wouldn't work since it would print 7 instead of the empty string


This looks dreadfully complex.


It's all type signatures. The actual function is seven lines.


This looks complexly dreadful.


Here is something similar I did in JS

https://gist.github.com/JakeCoxon/d78fa1debc13e46ae54a


I came up with nearly the same solution in swift:

https://github.com/zachcoyle/fizzbuzz-without-booleans/blob/...

But I like your solution better


You've qualified your approach in various ways downthread, but you can avoid ifs far more easily with two simple enums and two match statements. https://github.com/urschrei/rust_fizzbuzz/blob/by_reference/...


I also wanted to avoid match expressions as well


I'd hate to maintain that.


On the contrary, I'm fascinated by the prospect of a job where I'd be paid to abuse type systems to produce useless programs. :)


On the contrary, once you understand how it works, it's trivial to change the program

Let's say you wanted to change the program to yield "Fizzbuzz" instead of "FizzBuzz" in the 15 case, but the rest being the same.

I would first build my string from "fizz" and "buzz" and then I'd have to do .map(capitalize) as the second to last step. If I wanted to have the correct case in the originating strings I could just run them into to_lowercase first before anything happens and capitalize correctly afterwards.

(Actually, the capitalize function itself is probably more complicated than this change)

Similarly, other changes are intuitively easy to stick into the correct place in the program once you know what this program does.


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.

[1]: http://www.javaslang.io/


Conceptually this seems to be a Java approach to Railway Oriented Programming (http://fsharpforfunandprofit.com/rop/), which is pretty sweet.


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?


Try to up your reading comprehension before you question the competency of others.


Try 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.


The fact that you don't write "if" doesn't mean your aren't using it. Dann clickbait.


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.


I 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".


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.


And if you are mapping not to a value but to the execution of a statement, should that be map instead of for?


Well, I agree with that. If you're mapping use map instead of a for loop.


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 ...

It sounds like a ternary operator to me.


It'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


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.


Really 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.


It'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


It'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.


The 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.


This is still possible

java.util.Optional<int> opt = some_null_returning_function( );


Hey, 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.


Eh, 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)


clang 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.


> 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.


The Either type there is very similar to Rust's Result type.


Neither are new concepts, both are probably lifted from Haskell which probably got it somewhere else prior.


tl;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-...


In 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)


Any 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.


part two will be titled "Shaders and SIMD without If: scattering and gathering"


So, Scala.


Without the huge complexity of Scala and faster compile times. Java 8 is actually a surprisingly pleasant language to write.


  The 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


2 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.


I 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).


> If this API is not understood then the code feels convoluted.

I think there is more going on here. For the sake of eliminating an if, you are lifting everything else into what is effectively a separate language with the original code embedded in that language. Overall, that doesn't look like a win to me.

After all, what is the actual domain logic? Is it flatMap().map().flatMap().map()? Or is it validate().businessLogic().generate() ?

That doesn't mean that what is going in isn't useful, but it seems to me we need to have a way to specify the lifting without writing it down everywhere, so that the actual code can be expressed at the base level again.


I think replacing common usage patterns of generic control structures with named constructs adds clarity:

    for(blah blah blah)
tells the reader nothing more than there's going to be a loop, but

    map(blah blah blah)
tells the reader one sequence is going to be transformed into another by applying a function to each item. That's more informative to the reader if used for its intended purpose. It has the opposite effect if abused to repeatedly call a function mainly for its side-effects.

All programming techniques should be viewed as means to write code that's some combination of readable, reliable and performant, not as ends themselves.


> map() tells the reader one sequence is going to be transformed...

Except in this case it wasn't actually a sequence, but an Optional/Either and it wasn't about transforming that sequence but about error handling. So in a way the code wasn't even not intention-revealing, it was actually deceptive.


No, those functions were transforming the wrapped value. If there were no need to transform anything, you wouldn't need map or flatMap.


Not sure what the "No" is about, because the rest is a restatement of what I wrote. Yes, they are transforming the value that's wrapped in an Either/Optional, just as I wrote, and yes, the map/flatMap is necessary due to that construction.

However, just as I also wrote, that's not an actual sequence of values, it's a single wrapped value (or none).


The "no" was probably aimed at the fact that you were splitting hairs against their use of "sequence".

Optional can generally be thought of as a "sequence" of one or zero values. You could use an array of "[some]" or "[] /* none */" to manage control flow in an almost identical way (you'd just be adopting and managing an API that doesn't statically ensure that these arrays never have more than 1 value).

"map/flatMap" are predictable functions that can be used to manage the transformation of data (which is generally the entire point of code). The type/context that a computation is lifted into communicates a lot: Optional, for example, can be returned for a computation that may or may not return a value, and it can be "mapped" into another value by a pure computation that _always_ returns a value, or "flat-mapped" through a computation that may produce another nullable value. Anyone familiar with these basic functions can jump in, read the code, and generally know that every computation lifted into "map" can't fail, while every computation that is "flat-mapped" can. And in static languages it's all checked by the type system! No testing for "null" everywhere!


Right. I find that the easiest code to read is code written in a data flow style such that it's a series of transforms on data.


I'm not a functional purist, but I find that the functional 'principles' that I benefit the most from, in any context, are 1) massaging my data into the right shape with as few transforms as possible/practical/legible, 2) using pure functions whenever possible (in practice this often means accepting a few extra parameters in lieu of writing the code as a method; avoid 'this' in js, for example), and 3) starting with the data structures and seeing my app as, to a large degree, just a set of transformations on that (which usually is appropriate for the type of stuff I build).

I don't use partial application/currying that much, but more and more over time.

All in all I feel that doing these things have greatly improved my code and efficiency, and I'm kind of waiting for some downtime to get back into learning the more 'hardcore' functional languages.


That's about where I am. I like the idea of being a purist and see the potential gains but practical reasons such as needing to deal with other humans as well as my own lack of intelligence keeps it to this sort of midrange level


I don't think the 'map' is actually telling us anything very different than the 'for'. It is an issue I have with most articles in this vein, and this one is no different: they give simple, obvious examples that do not suggest that there is much practical difference between the approaches. We could do with some articles in which the examples are sufficiently well-developed that the advantages are clearly non-trivial.

It might be said that any such example would be too complex and drawn-out for an article like this, but if so, then to me that is like the drunk looking for his keys under the lamp-post: writing an article that claims a simple example shows a profound difference, when it does not, suggests that the Emperor's wardrobe is threadbare. The thing to do is to provide links to articles where this is worked out in sufficient depth, and I would like to see some of those links here.


Even though I share your concern, at least in my own code 'map' usually does tell me things that 'for' doesn't. Because if I were filtering a list, I'd use filter or reduce, if I were picking an element from a list I'd use pluck or whatnot, etc. So seeing a 'map' means that 99% of the time I'm transforming one set of values into another set without removing or adding elements.


Sure it does. For can be used for any of the following.

* Mapping (producing a data structure of the same size/type)

* Filtering (producing a data structure of the same type buy smaller)

* Side effects

* Collecting (producing a different data structure)

If you're incredibly unlucky or foolish a for loop might be doing more than one operation at a time!

Assuming you're only using the return types, map, filter, and collect are very clear about having only one purpose.

If that's not telling you more I don't know what is.


That's a fair point about map being constrained in the top-level structure of what it returns, but what map actually does depends on the function that is being mapped, and includes the possibility of more than one operation at a time - and if your language allows side-effects it can do that too. The point is that you have to look at the 'blah, blah, blah' to know what is happening (actually, the point of my original post is about extravagant claims of what simple examples demonstrate...)


Sometimes you can use bit-shift operations instead of multiplication and division, but you probably shouldn't unless you're an optimizing compiler or in a situation where you're doing the job of one by hand. Likewise, you probably shouldn't use a side-effecting function with map.


Exactly - the mere fact that map is being used does not automatically make things better; it can be abused just as a loop can (if using a side effect in a map would be a bad idea, the reasons for it being so would presumably apply to the equivalent loop.)

A more useful question is how can map, used properly, make things better, and I stand by my claim that the simple examples of the sort given in this article, and articles like it, fail to make that case.


I think map implies a data transformation without side effects. When I read 'map' in code, that's what I'm expecting. I expect I can replace 'map' with a parallel version that doesn't guarantee the order in which the elements will be processed and not change the semantics of the program. I expect that if I want to swap in an alternate function, I only need to be concerned with its ability to handle the inputs, produce the outputs and be a logically valid transformation.

I have no such expectations of a loop until I've read and understood its body. I hardly have any expectations of a loop at all until I've understood the whole thing.

And that's how map, used properly makes things better. It communicates what kinds of things the reader should expect it to do and not do. It reduces the cognitive load of reading the program, as if, for example, you're scanning for where a certain side effect happens, you can skip the function called by 'map' on your first pass through. It shouldn't happen there; maybe it could, but it should be pretty far down the list of places to check.


I don't think you are giving yourself enough credit here: it is you who has chosen to adopt good practices, not map that is obliging you to do so.

Mea culpa: I have to admit that I have sometimes used side-effects in map to modify the members of a list returned by a function reading data from a stream.


I think the semantics of "map" are quite a bit more specific than "for". For one thing, "map" functions have a return value which is usually the same length as its argument, which is usually a sequential data-structure. On the other hand, "for"-like functions or statements cannot or do not return values. True, if you ignore the return value from "map", it usually works the same as a "for" loop but the opposite is not true. The return value from 'map' is all the difference in the world and allows chaining of transformations amongst other things.


Putting aside languages in which loops may be expressions, you (and also village-idiot) are talking about how a program does things, not what it does, and what I am saying is that the case for map, etc. improving readability, reliability or productivity over the equivalent loop-based code is not made through simple examples. I am inclined to believe that functional programming is a better paradigm, but I did not arrive at that opinion from simple examples.


> For the sake of eliminating an if, you are lifting everything else into what is effectively a separate language with the original code embedded in that language. Overall, that doesn't look like a win to me.

Whenever I write Java (or Java-like OO), I always have this exact "separate language within a language" feeling.

I'm supposedly working in a high-level, object-oriented, loosely-coupled, message-passing/dynamically-dispatching world of classes and instances; yet an awful lot of code is actually written in a separate language of "primitive values" with opaque control structures like if/then/else, for/while, etc.

Compare this to e.g. Smalltalk, where "ifTrue" is a method on boolean objects, "timesRepeat" is a method on integer objects, etc.


Opaque control structures like if/then/else? They seem to be pretty clear when used in human languages.


"Opaque" as in completely uninspectable, unknown and unknowable to the language itself; as opposed to methods, which can be discovered, inspected and manipulated via reflection.


What real difference does it make whether repeat is a method on integer objects or whether it takes an integer argument?


Because a method on an integer object would mean it works in the same way as all library and application code; and therefore can be used as-is, or avoided by those who don't want it, or replaced if something better comes along.

Having "for" not be a method on an object means it's something completely different: a magical keyword control structure, a gift from the irreproachable language designers to the lowly language users; since mere users cannot be trusted to make such decisions for themselves.


Funny, I approach it from the other side.

Is the potential of failure part of my domain? Or is it something I have to handle in order to avoid errors?

Should .businessLogic() have to handle the case if .validate() failed? Or should it only be called if validate succeeded?

Using a common API (Either is not new or novel, it's all over the place) to separate success from failure lets me write code that is only concerned with its side of the success/fail tree without polluting it with null checks and error handling that belongs elsewhere.


Map, filter, fold, etc. are pretty much standard constructs and any decent programmer should be familiar with them.

There are subtle variations in naming like Java's Map is Select in C# because Microsoft modeled its functional API on SQL, but you have the same differences for iteration and selection (the things you are referring to for and if).

For example you have 'for (int i = 0; i < n; i++)' in C, but you don't have the exact same way of doing it in Python, where you have to use a range: 'for num in range(0, n):' or you can write a 'while' loop in Java but not in Go where it's a 'for' with a single expression.

I would choose a functional version over a three level nested for loop monster any day.


Let me be the one:

I'm a fairly decent programmer and I never worked with map or flatmaps outside of examples.

I have however significantly simplified two significant code bases, made and maintained for years a webapp that users loved etc.

Very often I feel it is just people wanting to sell something and it wouldn't surprise me if many of the people who sell functional are the same who sold object oriented back in the day.

This article was actually a bit refreshing IMO.


As programmers, we often confuse the means with the ends. We tend to over-emphasise the way in which code is written and we tend to forget about whether the code is doing the right thing.

That being said, I love map/filter/flatMap. Look at them as higher level loops.

For example: A while loop is very general, you can implement any kind of loop with it. This makes it harder to understand the meaning of the loop. Therefore, when you want to iterate a constant number of times, you'll likely use a for loop. It expresses your intent better. Map, filter, and flatMap are just extending this principle to more specialised use cases. The advantage for a reader is that the one word "map" already tells them a lot about what the loop body is good for.


This is the way I see it. A for loop is good if I'm counting, if I'm applying a function to every element in a list, I should express that.

An added bonus is that null elements are sorted and that you HAVE to separate data manipulation operations. You have to filter, map and apply sorting separately, not mix all three in a triple for loop with several branches.


Maybe it is a higher level of looping and certainly looks pleasing. However when it comes to using a debugger I have not found a way to avoid a higher level of effort.


What you say is true on the JVM. However, it is important to note that this does not need to be that way. One could imagine having the higher level loops and still debug them.

Of course, if you're writing actual code, now -- what I'm saying doesn't help you much. But if you're looking at a new language, it might be important to remember: not "filter" is broken that way, it's just the implementation on the JVM.


IntelliJ's debugger has decent support for working with functional code. Also the IDE can convert between map/filter/fold type functional pipelines and imperative loops automatically.


_Any_ debugger has _excellent_ support for working with imperative code. And not everyone uses an IDE, nor should it be a requirement.


I think this is replacing a formalism closer to natural language with one that is more abstract.

It's often justified to do that. For instance, we model stuff as matrices or graphs to gain access to their mathematical properties or because we have specialized hardware for matrix operations.

The big debate right now is whether the mathematical properties of functional programming are useful enough to move further away from natural language for general purpose programming.

We probably think something like this:

  Parse and validate the request
  If that fails then
    return 400, "invalid request: " + err

  Run business logic to get a result
  If that fails then
    return 500, "logic failed: " + err

  return 200, result as JSON
So should we use a formal language that looks similar to that or are there good reasons to use a chain of map and flatMap calls to hide the branching logic?


Reading 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!).


I 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.


I went from java to scala precisely because of the functional stuff. This was in the java 6 days so dunno if that's still be true if I were a java guy today




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: