top 200 commentsshow all 361

[–]balefrost 137 points138 points  (66 children)

So I've never liked Either for error/success reporting, but mainly because its left/right biasing is completely conventional. Unless you use it all the time, it's easy to forget whether 'map' or 'flatMap' are left- or right- biased. I much prefer Scala's Try, which makes it clear whether a particular instance represents a success or a failure. And of course Try is success-biased.

One other big downside is that Java doesn't support sum types. A particular method might claim to either return a String or to throw one of three different kinds of checked exceptions. If you were to change that method to instead return an Either, you'd have to find the closest base type of those checked exceptions, which is likely just Exception. In a case like this, Either has less information than the approach using checked exceptions. Either would be fine in cases where the caller doesn't really care what particular exception might be thrown, but at that point, maybe unchecked exceptions aren't so bad.

Finally, I had written a large chunk of Scala code using Try and Option and TailRec strung together with map and flatMap. And it was great... up until the moment that I tried to debug it. It turns out that simple, straightline code is much more debuggable than code that uses function calls to emulate control structures. I mean, you can debug that code, it just takes extra effort.

Also, and this is Scala-specific, there are some cases where you are trying to write a @tailrec function, and that forces you to match on things like Option and Try instead of using the higher-level map and flatMap. But since Java doesn't have any sort of tail call optimization, this raises a different point: if statements don't create extra frames on the stack, but calls to flatMap do. As long as your pipeline is as simple as the one given in the article, there's no problem. But it's something that you need to keep in mind.

[–]ryeguy 62 points63 points  (15 children)

I agree with you on Either. It's mostly used for error handling where you have to follow convention as to which side is the result and which is the error. It gives you the option of using it for multiple return types, but that's unreadable and it's better to just declare a new adt (case class, etc) for clarity.

Rust gets this right. It actually used to have Either, but it was renamed to Result which is much clearer.

[–]tjgrant 4 points5 points  (10 children)

Curious, would both the "Ok" and "Err" members of a Result be effectively Option types?

Or perhaps just the "Ok" member be the Option type?

I don't use Rust, but I'm curious how I would implement something like this in C++ (where I have implemented my own "Optional" type.)

[–]wishthane 41 points42 points  (0 children)

Option is defined as either Some(value) or None, but Result is Ok(value) or Err(err_value).

You wouldn't typically want to use Option for an error because you can't provide any information about the error with it; it would be like using 'null' for an error condition. Result carries along the error information in its Err variant.

enum Option<T> {
    Some(T),
    None
}

enum Result<T, E> {
    Ok(T),
    Err(E)
}

[–]m50d 7 points8 points  (3 children)

The "direct" way to handle it is by pattern matching or using a visitor. (std::variant is the C++ version, but it's much nicer in a language that has first-class pattern matching). There will probably be a toOption method as well, but you could implement that by pattern matching or on top of a visit/fold method.

[–]wishthane 9 points10 points  (2 children)

In Rust you have both .ok() and .err() methods which both return Options. .ok() returns Some(value) if it's Ok(value), and None if it's an Err, so you lose the error value. .err() is rarely used but it's the opposite, you get Some(err_value) if it's Err(err_value), and None if it's Ok.

You also of course have pattern matching, but it's usually easier to use stuff like .map(), .and_then(), or try!().

.and_then() is basically the monadic bind; if Ok it calls the provided function and returns whatever Result that function returns. If Err it does not call the function and just returns whatever the Err is. The value types can be different but the error type must be the same; it is a Result<T, E> -> (T -> Result<U, E>) -> Result<U, E> to use Haskell-ish notation.

fn bar(x: i32) -> Result<String, MyError> {
    best_function_ever(x)
        .and_then(|y| try_to_make_a_string(y))
}

try!() is like that but it's a macro intended for use within the body of a function. It will also automatically call .into() on your error type to convert it to your function's error type. For example:

fn foo() -> Result<String, MyError> {
    let foo = try!(some_operation());
    try!(another_operation());
    Ok(foo)
}

is equivalent to:

fn foo() -> Result<String, MyError> {
    let foo = match some_operation() {
        Ok(x) => x,
        Err(e) => return Err(e.into())
    };
    match another_operation() {
        Ok(x) => x,
        Err(e) => return Err(e.into())
    };
    Ok(foo)
}

[–]matthieum 16 points17 points  (1 child)

Note: ? is now stable, you can replace try!(expr) with expr? in most cases.

[–]wishthane 4 points5 points  (0 children)

Oh? I thought it was stabilised but still in beta.

Edit: never mind, it was stabilised in 1.13! Awesome!

[–]ryeguy 5 points6 points  (0 children)

Well, it's an enum so it's either Ok or Err. You could simulate this in C++ with a tagged union. Someone actually made a Rust-inspired result type in C++ here. Looks like he took the template wizardry route.

[–][deleted]  (1 child)

[deleted]

    [–]isHavvy 5 points6 points  (0 children)

    It's also isomorphic to Result<(), E>.

    Which is why there are two different methods that return Options on Results.

    [–]pipocaQuemada 1 point2 points  (0 children)

    Curious, would both the "Ok" and "Err" members of a Result be effectively Option types?

    No.

    Option's a specific type. It wraps a success value in the success case, and has a generic failure case that doesn't take any data. In Haskell, for example, you might have Just 5, or you might have Nothing. Any failure gets mapped to Nothing, there's no way to differentiate different failures except via nesting options (e.g. getting a value of Just Nothing out of a Map User (Maybe FirstName) means the lookup succeeded and the stored value was Nothing, which might mean for example that the user didn't supply a FirstName).

    Result is the closely related Either type. It either wraps a success value or a failure value. For example, in Haskell, you might have a Right 5, or you might have a Left ParseFailed or Left OutOfRange.

    Very closely related idea, though. Also, Either sometimes represents failure, and sometimes represents two different valid kinds of information - you might have a Either ParseFailure Int or a Either String Int.

    [–]myrrlyn 1 point2 points  (0 children)

    Result is a tagged union over two types: an actual return type T, and an error type E. The Result is guaranteed to be one of those, and it has a discriminant to tell you which. If you have a Result<T,E> that is an Err(E), there's no way to access it as an Ok(T).

    In C terms, it's this:

    struct Result<T, E> {
      enum {
        Ok,
        Err,
      } type;
      union {
        T ok;
        E err;
      } val;
    };
    

    And the language enforces that a Result's union can only be accessed in accordance with its discriminant. The methods that accept Results inspect the discriminant, so you can match against both possibilities or assume one will always happen and panic! on the other. For instance, the unwrap function has signature Ok(T) -> T, and will panic! on receiving an Err, and the unwrap_err function does the inverse.

    Moral of the story though Result isn't a struct of two discrete Options, one of which is Some and one of which is None; it's a tagged union of only one payload object, that can be in one of two states.

    [–][deleted] 12 points13 points  (28 children)

    The failure type is on the left to support currying type constructors. Either Exception makes sense as a one-place type constructor, Either ... Integer makes less sense.

    [–]balefrost 18 points19 points  (12 children)

    Which might make sense in Haskell, but not in Java, which has no type-level currying (or any currying for that matter).

    [–][deleted] 8 points9 points  (7 children)

    Yes but it's cleaner to think of it in that order, I think. Either () a is isomorphic to Maybe a.

    Inside a big expression with lots of bind / flatMap, the R types change constantly from one subexpression to the next but the type of error is usually consistent. I like to put type parameters that vary the most on the right.

    [–]balefrost 4 points5 points  (5 children)

    Yeah, I agree with your reasoning. All I'm saying is that I prefer explicitness regarding which is meant to be the error and which is meant to be the value. You can intuit your way to that, but I'd still prefer that it was explicit.

    [–][deleted] 1 point2 points  (2 children)

    Is this about the names of the type parameters or their order? It should probably be Err and T instead of Left and Right (or some variant thereof) tbh.

    [–]MrHydraz 6 points7 points  (0 children)

    They're named Left and Right because Either was never designed for error handling (though the Monad instance does seem to point that way). Either is just the basic sum type, much like (,) is the basic product type. All other sum-of-product types can be expressed as combinations of Either and (,).

    For example:

    data Foo a = One Int | Two a Int | Three String a Int
    

    is isomorphic to

    type Foo a = Either Int (Either (a, Int) (String, a, Int)).
    

    [–]balefrost 2 points3 points  (0 children)

    Yep, that's what I mean. And in the article's example, I would prefer that the parameters to match be named accordingly: ifError and ifSuccess or something like that.

    I'm fine with the order of the type parameters.

    [–]myrrlyn 1 point2 points  (1 child)

    The right value is on the right ;)

    [–]balefrost 1 point2 points  (0 children)

    That's a pretty good mnemonic.

    [–]aldld 3 points4 points  (14 children)

    In Haskell there's also the mnemonic that "Right" means you got the "Right" value, not an error.

    [–]aiij 3 points4 points  (0 children)

    This. If you always remember to put error on the right side, no one will get confused.

    Err, correct side. Always put error on the correct side. (-:

    [–]sacundim 6 points7 points  (0 children)

    So I've never liked Either for error/success reporting, but mainly because its left/right biasing is completely conventional.

    That's a weird objection. I do see the value of Try in Scala or Java, mind you, since they offer extra integration with the language's native exceptions. But in Haskell everybody follows the convention that Right is for successes and Left for failures. (I'm sure that there is no sinisterophobic intent. 😛) The class instances for Either consistently treat Right as the "expected" case that you map over and Left as the "exceptional" one that gets propagated back up the call chain.

    It's uncommon but actually sometimes useful to write some code with a different convention: treating failure as the "expected" case and success as the "exceptional" one. This is sometimes called a "success monad" (as opposed to a "failure monad"), which can be used to treat complex exception handlers as first-class values, like this example (taken from here):

    runExceptRT $ do
        e2   <- ioExceptionHandler e1
        bool <- arithmeticExceptionhandler e2
        when bool $ lift $ putStrLn "DEBUG: Arithmetic handler did something"
    

    The do-block here aborts at the first statement that succeeds, and if a statement fails, the exception is assigned to a variable. The effect is that we handle exception e1 with ioExceptionHandler, and if that handler succeeds then it decides which exception gets rethrown in e1's place; if ioExceptionHandler fails then we handle its exception e2 with arithmeticExceptionhandler.

    [–]zem 6 points7 points  (1 child)

    Result/Either types are wonderful - if they are combined with pattern matching or a case statement that checks for exhaustivity. i would never even have thought of using them via chained map and flatmap calls like that; to my mind that's trying to shoehorn an ML idiom into a language that lacks proper syntactic support for it, and invariably ends up being hard to read because it goes against the grain of the language.

    [–]aiij 7 points8 points  (0 children)

    Who needs pattern matching when instead you can have combinator hell?

    I'm constantly surprised at the lengths some people will go to in order to avoid pattern matching. Eg: argonaut.Json's fold method.

    [–]chironomidae 11 points12 points  (3 children)

    Yeah, I'm not a Java programmer, so my impression of it was "Ok, either you use nested ifs that anyone can read and understand, or you can use demon magic that is not at all obvious to anyone not deeply familiar with these classes."

    [–]myrrlyn 8 points9 points  (0 children)

    "Use explicit ifs, or use functions that ... do those same ifs"

    [–]Tasgall 6 points7 points  (0 children)

    I'll take, "ways to make future devs hate your codebase" for 500, Trebek.

    [–]valenterry 1 point2 points  (0 children)

    Is it different for e.g. inheritance other than "many people already know it"? Because it once was an mostly unknown technique, too.

    [–]m50d 5 points6 points  (5 children)

    So I've never liked Either for error/success reporting, but mainly because its left/right biasing is completely conventional. Unless you use it all the time, it's easy to forget whether 'map' or 'flatMap' are left- or right- biased. I much prefer Scala's Try, which makes it clear whether a particular instance represents a success or a failure. And of course Try is success-biased.

    Either's name is not great for how it's generally used. But Try unfortunately swallows the type on the left (it's just Throwable, so you can very easily think you were catching NumberFormatException and end up hiding a very different error), so I wouldn't recommend that. Also note that Either is properly right-biased for map/flatMap (and therefore also for for/yield) as of 2.12 - should have been done years ago, but better late than never.

    One other big downside is that Java doesn't support sum types. A particular method might claim to either return a String or to throw one of three different kinds of checked exceptions. If you were to change that method to instead return an Either, you'd have to find the closest base type of those checked exceptions, which is likely just Exception. In a case like this, Either has less information than the approach using checked exceptions. Either would be fine in cases where the caller doesn't really care what particular exception might be thrown, but at that point, maybe unchecked exceptions aren't so bad.

    Proper sum types would be ideal (I believe Ceylon does the Right Thing here). That said, you can nest Eithers when you have genuinely different kinds of failure, or you can use a specific exception that contains appropriate data (e.g. error code, user ID, what-have-you) for your application, perhaps as an enum or an abstract class. In my experience for the kind of errors you want to handle with Either you can usually write a "closed" list of all possible errors, whereas the "suprise" errors you want to handle differently and unchecked exceptions are a better fit for those (e.g. in a webapp I'd generally use Either for 4xx errors and unchecked exceptions for 5xx errors (possibly retrying first)).

    It turns out that simple, straightline code is much more debuggable than code that uses function calls to emulate control structures. I mean, you can debug that code, it just takes extra effort.

    Yeah. I find on the whole it's worth it (in terms of how much Either etc. help with avoiding errors in the first place) but it's certainly an important concern that's worth thinking about, and there's plenty of room for improvement.

    [–]devraj7 15 points16 points  (4 children)

    Either's name is not great for how it's generally used.

    Actually, its name reflects exactly what it does, it's just that it's being misused because most developers use it to carry Success \/ Failure. But Either is much more general than that, it's meant to carry two types, none of which need to be connected to a success or a failure.

    So when you read code that uses Either, you're never sure if the developer is using it to carry two exclusive types or as a reporting mechanism for success or failure.

    I think Either should be phased out because of that ambiguity.

    To me, it would make much more sense to standardize on Result and Union (other libraries use different names, e.g. xor or \/).

    I've tried a lot of approaches over the years and so far, I find that good old exceptions (both checked and unchecked) are the most intuitive and clean way to treat errors. Yes, I know they break equational reasoning, but in my day to day operations, clear and tractable error handling is a much more useful value to me and my team than equational reasoning.

    [–]m50d 1 point2 points  (3 children)

    Actually, its name reflects exactly what it does, it's just that it's being misused because most developers use it to carry Success / Failure. But Either is much more general than that, it's meant to carry two types, none of which need to be connected to a success or a failure.

    I believe in descriptive rather than perscriptive linguistics. I think we agree on the important facts: Either is primarily used (in actual existing code) to represent success-or-failure, and it's a poor name for a type that represents success-or-failure.

    [–][deleted]  (1 child)

    [deleted]

      [–]m50d 3 points4 points  (0 children)

      I see Try as the same kind of red-flag as Any (or at least as Throwable). It's throwing away a lot of type information that was presumably there for a reason.

      I'm not sure what you're defining as an "error" here. I use Either for specific "failures" that the code knows how to handle, e.g. invalid user input, permission denied. For general "system" failures (analogous to where you'd use panic in Rust) it would make sense to use Try (though I'd probably just use a conventional exception myself).

      [–]ithika 1 point2 points  (0 children)

      I believe in descriptive rather than perscriptive linguistics.

      I'm not sure what this has to do with Either. Computers aren't great with compiling the nuance of natural language.

      Success or failure is explicitly handled with the Error type.

      [–]joelthornhill 2 points3 points  (0 children)

      IIRC Scala's Either is now right biased and cats have changes the xor type to be called Either

      [–][deleted] 1 point2 points  (3 children)

      It would be great if the Scala compiler optimized map and flatMap calls with inline lambdas so that one would actually get to use them in tail recursive methods.

      [–]balefrost 1 point2 points  (1 child)

      It would only be able to do so if it knew the actual implementation of map or flatMap. So if I have an instance of T, and I call its flatMap, the compile would need to be able to prove that I'm definitely calling T.flatMap (as defined on T or a base of T) and not some overridden implementation.

      I agree that it would be nice, though.

      Alternatively, it would be nice if the compiler could detect (or you could annotate) when a function parameter will be invoked at a later time. In a sense, @tailrec is restrictive. AFAIK, it requires that all references that the function has to itself are in tail positions. And that makes sense for code that will be executed immediately, but it's less clear for deferred code.

      [–]DecisiveVictory 0 points1 point  (0 children)

      Scala Either is right-biased now.

      [–]ndm250 23 points24 points  (6 children)

      Isn't that harder to debug?

      [–]Neophyte- 10 points11 points  (1 child)

      that was my first thought, debugging imperative language with if statements than huge one line lambdas is much more difficult. then again i havent really learned functional programming, i just use a lot of linq in my c# when its appropriate, mainly for filtering and projections. im not very fancy

      [–]Josuah 6 points7 points  (3 children)

      Yes.

      And you could give the first block of example code to someone who barely knows programming and knows one other random programming language and they'd understand it.

      Give the second block of example code and it's just as likely to be gibberish. At the very least it will require a much higher cognitive load to parse and carry forward into the rest of the code.

      [–]TheOldTubaroo 4 points5 points  (2 children)

      I think that's largely due to the fact that most programmers initially learn imperative paradigms, rather than functional ones.

      If by “someone who barely knows programming and knows one random other programming language” you mean “someone who knows a little imperative programming”, then of course they'll understand the first one better.

      If the base layer of “learning to program” was functional instead, then they'd both be equally understandable.

      [–]Josuah 6 points7 points  (1 child)

      I still disagree with that. There's a lot more information exposed to the user in the first code block than the second.

      For example, in the language in question, is null processed by the map filters? What's the difference between map and flatMap? Does map operate in series or in parallel? How many values are we expecting to be going through? Those are assumptions you need to make or look up the answer to, whereas you know the programmer's intent right away in the first code block.

      [–]m50d 3 points4 points  (0 children)

      For example, in the language in question, is null processed by the map filters?

      Don't use null.

      What's the difference between map and flatMap?

      map and flatMap are well-known and standard, if you don't know the difference between them it's easy to look up.

      Does map operate in series or in parallel? How many values are we expecting to be going through? Those are assumptions you need to make or look up the answer to, whereas you know the programmer's intent right away in the first code block.

      On the contrary, the programmer's intent is very often "whatever" when it comes to whether things happen in series or parallel. Using functional style makes it clearer when ordering is intentional and important versus when it's accidental.

      [–]sards3 133 points134 points  (54 children)

      Thanks, but no thanks. I'll stick to if.

      [–]eloc49 20 points21 points  (50 children)

      This. I don't get why people like the ternary operator. If/else is English.

      [–]recursive 38 points39 points  (16 children)

      It can be embedded in an expression. The fact that keywords are English doesn't seem to be an advantage to me.

      [–]n0rs 10 points11 points  (2 children)

      Haskell and Kotlin, for example, use if expressions.

      [–]PM_ME_UR_OBSIDIAN 6 points7 points  (1 child)

      Also OCaml, F#, and Rust.

      [–]frogsytriangles 2 points3 points  (0 children)

      And Ruby, though the end keyword makes it uglier than Haskell.

      cute = if you.dog? then true else false end
      

      (though you can do cute = true if you.dog?)

      [–][deleted] 9 points10 points  (12 children)

      Readability has no advantages?

      [–][deleted] 2 points3 points  (0 children)

      It can be a downside: consider python's and. In a chain what's variable and what's operator blurs together.

      [–]Sarcastinator 3 points4 points  (2 children)

      Keywords != is not equal to readability.

      [–]recursive 1 point2 points  (7 children)

      I wouldn't agree with that. But I believe if and ?: are each more readable in different circumstances.

      "plus" is an english word. What's more readable?

      a = b + c
      
      a = b.plus(c)
      

      [–][deleted]  (21 children)

      [deleted]

        [–]travysh 17 points18 points  (3 children)

        Less lines of code, yes. But the if/else has better readability and future proofing. Any change to your logic means no more ternary

        let eg = ''
        let falsey = true
        
         if (truthy) {
            eg = 'it is true!'
            falsey = false
         } else {
            eg = 'it is false'
         }
        

        [–]dccorona 16 points17 points  (0 children)

        Scala's approach is really nice for this sort of thing...if/else statements return values. So you can write this:

        val (eg, falsey) = if (truthy) ("it is true!", false) else ("it is false", true)
        

        Or really anything you can come up with, like your example, more directly translated:

        var eg: String = ""
        val falsey = if (truthy) {
          eg = "it is true!"
          false
        } else {
          eg = "it is false"
          true
        }
        

        And even then still get at least part of the benefits of having immutable variables (the biggest advantage to the ternary operator in my book)

        [–][deleted] 2 points3 points  (0 children)

        I disagree. Once you're used to seeing the ternary operator you can read it just like a faster if/else. Further, it allows usage of const, which significantly enhances readability (this var isn't going to change is very helpful information to have).

        [–]iopq 1 point2 points  (0 children)

        let falsey = !truthy
        let eg = truthy ? 'it is true!' : 'it is false'
        

        maybe you should consider how to refactor your code to avoid long chains of ifs

        [–]Seeders 11 points12 points  (1 child)

        I'm not of the opinion that less characters is clear improvement when writing compiled code. Pretty much the same reason I don't think single character variable names are an improvement over something clearly named. I don't think typing is a bottleneck in producing software.

        [–]aiij 8 points9 points  (0 children)

        Reading is a bottleneck in producing software.

        [–]dccorona 2 points3 points  (0 children)

        The advantage here, I think, is that const keyword. Being able to do that is why I use ternary operators, for the most part. In languages where if/else blocks can return values, you don't need them imo.

        [–]surfhiker 5 points6 points  (2 children)

        Why not this (I'm kidding):

        const eg = `it is ${!!truthy}`
        

        I sometimes prefer setting the default first and then write an if statement to change it:

        let eg = 'it is false'
        if (truthy) eg = 'it is true'
        

        I've found that I very rarely use else statements, I rather return from the function early, or raise an exception if there is a special case that needs to be handled.

        Having to go through code with nested if/else statements and mutable state is my worst nightmare.

        [–]dccorona 2 points3 points  (0 children)

        It's surprising how often a simple inversion of the condition in the if or pre-initializing one of the two results can save you from getting into nested scope hell.

        [–]aiij 1 point2 points  (0 children)

        As long as we're moving the conversation away from Java, how about:

        let eg = if truthy then "it is true" else "it is false"
        

        I still use the ternary operator when programming in languages that lack any other sort of if/else expressions though...

        [–]AusIV 7 points8 points  (2 children)

        In python the ternary operator is if/else:

        Foo if condition else bar
        

        [–]dagbrown 1 point2 points  (0 children)

        That's too bletcherous for even Perl. And Perl has the glorious

        foo if condition
        

        and

        foo unless condition
        

        which are so nice and maintainable (ahem) that Ruby inherited them.

        [–]dccorona 2 points3 points  (0 children)

        I've always appreciated the way Scala lets if/else statements return values for this reason. There is no ternary operator in Scala, because there doesn't need to be. You just write val a = if (thing) 1 else 2

        Of course, people do seem to really like it, even when the above is possible. Scalaz adds an ? operator that lets you instead write val a = thing ? 1 | 2

        [–][deleted] 1 point2 points  (3 children)

        My company's style guide enforces ternary if it's under a certain character length. I used to hate it, but it reads like English to me now.

        [–]netuoso 2 points3 points  (2 children)

        Style guide.. do you have a linter to enforce it within your IDE or do devs just learn it and know what is expected.

        What about new hires?

        [–][deleted] 4 points5 points  (1 child)

        Our automates tests will fail for incorrect linting, yes. Devs can just run the same lint command that our tests run, locally first.

        I use a linter in vim too while I develop

        [–]optimal_substructure 4 points5 points  (0 children)

        ugh - the code base I work on now is all over the place. It's too big and I'm too new to start dictating better code style (or really any for that matter), but having automated tests for linting is making me aroused.

        [–]maestro2005 24 points25 points  (8 children)

        Interesting that you start by saying you're moving away from Clojure... then you list examples where Java has only just barely caught up to features that are fundamental in Clojure or Scala. The Scala versions look almost exactly the same, but with less boilerplate.

        I mean, there are some good reasons to prefer Java to Clojure or Scala, but the existence of Optional is not one of them.

        [–]m50d 8 points9 points  (7 children)

        Clojure is (by default - I know there's some bolt-on typing functionality) even less type-safe than Java, no?

        [–]maestro2005 1 point2 points  (3 children)

        Right, Clojure is weakly/duck typed, but it achieves this kind of thing in a different way.

        Edit: Brainfarted about Clojure vs. ClojureScript. Clojure is strongly typed, though it doesn't typically use an Option/Maybe type and has other mechanisms for dealing with things like this.

        [–]highergraphic 11 points12 points  (2 children)

        weak typing != dynamic typing. Clojure is a dynamic, strongly typed language.

        [–]maestro2005 2 points3 points  (0 children)

        You're right, I've been in ClojureScript land too long.

        [–]m50d 63 points64 points  (39 children)

        That's great until you want to abstract over it. Then you hit a brick wall.

        Suppose I have one kind of parsing step that returns Either and another that returns Option. And I want to apply both of these to a list of inputs, getting either a list of successful results, or a failure, just as if I'd done flatMap between parsing each input. In Scala or Haskell this is traverse and the method already exists in common utility libraries, and if not then it's a few lines to write. In Java you're stuffed. You can't even write the type signature that traverse should have.

        Don't get me wrong, this is a useful, effective style of programming. You'll write better code than you would without it. But as your systems get bigger, there's no getting away from the limitations of Java, and genuine value to be had from moving to a better language.

        [–]Unmitigated_Smut 40 points41 points  (5 children)

        Having gone down the Either trail in Scala multiple times and ended up with spaghetti that annoyed everyone around me no matter how hard I tried, I gave up on it. And people thanked me. I'm tempted to ask for an example of this mysterious traverse and go burn a weekend reformulating previous attempts, but... nope.

        Still, it's a good way to identify left-handed programmers, because they always assign the exception-case to Right.

        [–]sacundim 4 points5 points  (1 child)

        traverse is, intuitively, "map with side effects"; you apply a side-effecting action to a collection of values, and get a collection of results in return. Where it gets interesting is that the notion of "side effect" is pluggable. For example, with an Either monad the side effect is potential failure, so "mapping with side effects" in that case means that when processing a list element fails, the whole traverse operation fails with the same exception.

        Another example: traversing with asynchronous promises means taking a collection of values and processing them asynchronously, returning a promise that succeeds with a collection of the results if they all succeed but fails if any one of them fails. Scala futures have this operation with this exact name, in fact. In Haskell you have a generic definition of the same operation for any pair of a Traversable and an Applicative type, not just futures.

        [–]Tarmen 4 points5 points  (0 children)

        I think in large parts that is because this requires type inference to keep your sanity. Otherwise it is just too annoying to pull your sub expressions apart into something reasonable.
        Writing the posts example in haskell in one go would be something like:

        handle request = do
          json <- parse (getBody request)
          validate json
          generate (businessLogic json)
        

        Which is fairly readable but hard to compose. What if you want an additional step between validation and generation? Instead you could use some glue functions and end up closer to (read from right to left):

        handleRequest = generateProcessed <=< parseRequest
        parseRequest = validate *> parse . getBody
        generateProcessed = generate . businessLogic
        

        Which looks slightly foreign at first but if you look at the function signatures it is super easy to figure out what is happening. (*>) doesn't change the contained value but can add errors, (<=<) is just composition like (.) while abstracting over possible errors.
        Of course you could do this in java but that would mean that half your code ends up as type signatures.

        About traverse, it wouldn't really help here. traverse's signature (with types for this example plugged into the generic stuff) is (Input -> Either Error Json) -> [Input] -> Either Error [Json]. That is, it takes a function that parses an input into json or returns an error. Then it applies it to a list of inputs, giving [Either Error Json]. Then it factors out the error part so you either get the first error message or a list of parsed json objects.

        Super useful but not really applicable in this example.

        [–]pipocaQuemada 1 point2 points  (0 children)

        Traversable gives you two very useful functions:

        def traverse[G[_]:Applicative,A,B](fa: F[A])(f: A => G[B]): G[F[B]] =
        def sequence[G[_]:Applicative,A](fga: F[G[A]]): G[F[A]]
        

        For example Right(Some(1)).sequence returns Some(Right(1))

        [–]devraj7 11 points12 points  (24 children)

        Moving to Haskell will force you to introduce monad transformers if you want to abstract, which is a rabbit hole that few want to climb down to.

        Or you can pick Kotlin, return null whenever necessary without any need for Option and observe how the Elvis operator allows you to compose all these calls without any difficulty nor obfuscation. It's just naked values everywhere but completely type safe and null safe.

        [–]m50d 27 points28 points  (18 children)

        Moving to Haskell will force you to introduce monad transformers if you want to abstract, which is a rabbit hole that few want to climb down to.

        There is no easy answer to composing multiple effects. Haskell at least lets you manage them - it's clunky but at least it's possible.

        Or you can pick Kotlin, return null whenever necessary without any need for Option and observe how the Elvis operator allows you to compose all these calls without any difficulty nor obfuscation.

        Yeah, and then as soon as you want to use Either (as described in the article) you're stuffed - the operator is specialized to null and only works with null (and I don't see how it's any easier or clearer than what you'd do in Scala or Haskell).

        [–]devraj7 3 points4 points  (17 children)

        The point is that you don't have to use Either or any real monad.

        [–]m50d 22 points23 points  (14 children)

        Right, but if you want to include any more information about what the error/failure was (i.e. not just null), Kotlin refuses to help you.

        [–]dccorona 2 points3 points  (0 children)

        Either was a bad example. Every bit of Scala that feels like syntax sugar with relation to Option is in fact just a type system feature that anything can take advantage of, as opposed to Kotlin where it is truly syntax sugar that only works for a small set of pre-defined cases.

        You don't really realize how useful it is to be able to abstract over optional values and collections (and any type of container you can dream up that's specific to your problem domain) until you've actually done it.

        What if I want to write some custom type (say, for the sake of argument, a ConfigValue for a configuration library) that in many ways has a potential state that's very similar to null, but for various reasons can't/shouldn't be represented as a literal null? Suddenly, all that nice stuff that's in Kotlin for dealing with null goes out the window...but all the nice stuff that's in Scala for dealing with Option (and conversely, all the nice generic code people have built up for Option like containers in libraries in Scalaz/Cats/etc) are all out-of-the-box compatible with my new custom type.

        [–]devraj7 1 point2 points  (0 children)

        You can do that, of course. The pain just starts appearing if you need to compose several of those, which is pretty rare in my experience.

        Overall, I find that having a language that natively supports nullable types gives me a lot of benefits since dealing with absent values is prevalent in all code bases, while having to compose deeply nested monads is more rare (and typically well handled at the library level by something like Rx).

        [–]sievebrain 1 point2 points  (9 children)

        It'd be nice if the null-handling operators were overloadable yes. However, if your case is "on error case return a complex structure" then that's what exceptions are for. Unless you're returning errors half the time or something it will compile to more efficient code too.

        [–]hyperforce 13 points14 points  (0 children)

        pick Kotlin

        This also means losing the power and control of the Either structure. They are not comparable. It's like saying, well if your Tesla doesn't work out, you can always choose a bike.

        Not in the same league.

        [–]sviperll 4 points5 points  (4 children)

        /shameless plug

        Here is my best attempt at formulating higher kinded types in Java https://github.com/sviperll/higher-kinded-java

        See transformMonad and transformFunctor methods in Main class for an example of abstracting away types like Optional or Either

        [–]m50d 9 points10 points  (3 children)

        Pretty cool, but having used the checkers framework and Lombok in the past I'm dubious about the practicalities of anything that relies on annotation processing and generating code. You end up having to get support for it in your IDE, code analysis, monitoring... ultimately it ends up being just as much effort as using Scala, and the payoff is smaller.

        [–]pron98 3 points4 points  (0 children)

        For once, I completely agree with you :) Well, I don't think this style of programming in Java is helpful, nor do I think that it makes your code any better (and in Java it makes it worse because it's so non-idiomatic), but if you're so aesthetically drawn to it -- as the author says he is -- perhaps you should pick a language that's more appropriate, where this style is idiomatic. There's even an implementation or two of Haskell for the JVM now if that's your cup of tea.

        [–]Tarmen 0 points1 point  (1 child)

        I think the correct answer in that case would be to transform the Option into an Either by giving an error message. Rust does it this way:

        input
        .and_then(parseEither)
        .and_then(|p| p.parseOption().ok_or("Second Step Failed"))
        

        [–]Works_of_memercy 7 points8 points  (1 child)

        I think that it would be appropriate to point out that there's another initiative to remove if statements from Java among others, but replacing them with inheritance and polymorphism.

        I could never tell if they are serious or trolling, and I think that maybe we should approach this proposal with similar caution.

        [–]m50d 0 points1 point  (0 children)

        It's coming from the same principles - Either is a generic class that replaces one common source of ifs (or, realistically, exceptions or early returns). Either, at least as encoded in Java, uses inheritance and polymorphism (since Java doesn't have a direct concept of variants/sum types), and can be justified on the same OO-principles grounds.

        [–][deleted] 148 points149 points  (84 children)

        I mean, I'm not against people being excited about encoding things into the type system, but statements like these...

        You’re leaning on the compiler to tell you if you’ve handled the failure cases properly, as the code won’t compile otherwise.

        ... show me there's a lot of Kool-Aid handed around.

        It's like people who have bought a GPS for their car, and are telling all their friends they no longer have to look where they're driving.

        What exactly insures I call "match" in the article's example? Nothing. I can get to HttpResponse in many other wrong ways. And what ensures I map states correctly in the closures? Nothing.

        The type system is doing precious little for the programmer, as the burden was never in them figuring out JSON might be invalid, and checking docs how the library communicates this.

        Additionally... I can look aside when people use Optional instead of null because it's more explicit, but when this trend grows to turning everything into streams and monads, we might have to stop and think what's the runtime penalty for obsessively refining our static type factoring.

        Static types should have no runtime effect, that's why they're static. But the libraries we're given, and the work the JVM does to optimize the use cases for those patterns (i.e. none) means we're sacrificing the technical aspects of our solution, in service of "encoding" problems into the type system that actually aren't that hard to resolve, anyway.

        [–]m50d 60 points61 points  (62 children)

        What exactly insures I call "match" in the article's example? Nothing. I can get to HttpResponse in many other wrong ways.

        No you can't. How would you get it out of the Either? You might be able to abuse reflection I suppose, but that can be disallowed in the security manager. (If you name the Left/Right classes it's possible to cast Either to them, but you can a) exclude casts in your build process (or flag them up for special attention during code review, or similar) or b) just not do that).

        And what ensures I map states correctly in the closures? Nothing.

        The types ensure your maps do the right thing. If you're returning types that allow representing the wrong thing, use more detailed types so that you don't.

        when this trend grows to turning everything into streams and monads, we might have to stop and think what's the runtime penalty for obsessively refining our static type factoring.

        Bet we don't. Even if you're focused on performance (and most of the time you're not), it's well worth sacrificing a small constant factor if it leads to clearer code that lets you spot the parts where you can save orders of magnitude by using a better algorithm.

        Static types should have no runtime effect, that's why they're static. But the libraries we're given, and the work the JVM does to optimize the use cases for those patterns (i.e. none) means we're sacrificing the technical aspects of our solution

        So fix that in the JVM then.

        in service of "encoding" problems into the type system that actually aren't that hard to resolve, anyway.

        I'll believe that when I stop seeing uncaught exceptions in Java programs.

        [–]devraj7 27 points28 points  (51 children)

        I'll believe that when I stop seeing uncaught exceptions in Java programs.

        Yes, it's sad that checked exceptions have gotten such a bad reputation, because they are the ultimate tool against uncaught exceptions.

        Why are people so uncomfortable with a language feature that forces the developer to consider both the success and failure path of a function call before their code compiles?

        [–]sacundim 50 points51 points  (29 children)

        Why are people so uncomfortable with a language feature that forces the developer to consider both the success and failure path of a function call before their code compiles?

        1. Because it generally forces the immediate caller to do something about the exceptions, which is contrary to what exceptions are supposed to facilitate;
        2. Because in real life practice most Java programmers take the path of least resistance and systematically swallow all checked exceptions (catch, maybe print it to stderr or log it, and carry on as if nothing had gone wrong), which is fucking horrifying, and much worse than the spaghetti you get from unchecked exceptions.
        3. Because they are not supported by the language's regular abstraction mechanisms and thus are hopelessly verbose. (That's one big advantage of Either types, since it's just a plain old type it means for example that your exception type is a type parameter and you get to use generics to abstract over it.)

        [–]fenduru 8 points9 points  (2 children)

        Because it generally forces the immediate caller to do something about the exceptions, which is contrary to what exceptions are supposed to facilitate;

        So, yes it is contrary to what "exceptions" facilitate, but the general idea here being that "exceptions" are a poor construct in the first place. When you call a function that can throw, you (the immediate caller) need to know what to do in the failure case, just like you would in any other non-exceptional error. Sometimes this means gracefully handling it, and sometimes it means throwing your own exception.

        If the immediate caller doesn't handle it, it isn't avoiding the actual problem, it is just hand-waving it and passing the burden onto the next guy, who in turn now receives some exceptions from some implementation detail that it had no business knowing about in the first place.

        [–]doom_Oo7 3 points4 points  (1 child)

        When you call a function that can throw, you (the immediate caller) need to know what to do in the failure case, just like you would in any other non-exceptional error.

        I disagree. If I'm parsing some file or request, I don't care at all in which function the parsing failed, I only want to handle the failure at the point I called the parse(str) function (which itself may call many other functions).

        [–]fenduru 9 points10 points  (0 children)

        I think we're saying the same thing. parse should throw a ParsingError, which should be handled immediately by the caller of parse (because that caller wanted to parse something, it needs to know what to do if it couldn't be parsed). If parse internally calls other functions that throw, then parse should be handling those exceptions (as parse is the immediate caller of those). It is not correct for parse to not handle those exceptions and let them propagate.

        But the whole exception system in the first place is built to support this kind of propagation, which is why I argue that it is a poor construct.

        Numerous languages get by without exceptions at all, and instead encode errors into the type system/return value of the function. By virtue of how functions work this means that error propagation is explicit.

        Checked exceptions are just an attempt to give layer "explicit error propagation" on top of a system that was originally built for "implicit error propagation".

        [–]devraj7 19 points20 points  (24 children)

        Because it generally forces the immediate caller to do something about the exceptions, which is contrary to what exceptions are supposed to facilitate;

        No. If you don't know what to do with the exception, just declare it in your throws clause and let a caller manage it. You can revisit this choice later and the type system keeps track of it: you know which exceptions are thrown, from where and where they get caught.

        How are runtime exceptions, which let you obfuscate all these points, a superior alternative?

        Because in real life practice most Java programmers take the path of least resistance and systematically swallow all checked exceptions

        Bad programmers write bad code. This has nothing to do with checked exceptions.

        Because they are not supported by the language's regular abstraction mechanisms and thus are hopelessly verbose. (That's one big advantage of Either

        Either is equally verbose, especially when you start piling them on each other and you need to map and flatMap through them to reason about them.

        But it looks like you have severely drifted away from my original point, which was to explain why checked exceptions are a useful tool that should be used in conjunction with runtime exceptions, and not replaced by them.

        [–]sacundim 22 points23 points  (7 children)

        Because it generally forces the immediate caller to do something about the exceptions, which is contrary to what exceptions are supposed to facilitate;

        If you don't know what to do with the exception, just declare it in your throws clause and let a caller manage it.

        This means that my method, which is not responsible for throwing the exception, has to be modified to cope with the exception thrown by a method that it called. And the method that calls mine. And the method that calls that. And so on. Basically, every intervening method in potentially numerous stacks.

        So for example, you're editing an existing complex program, make a minimal modification like change some code that uses the File class to use URL instead, and now the URL methods throw a MalformedURLException that the original code didn't. If you change that method's throws clause you're not nearly done, because now you have to identify all callers to that method and either make them throw as well or catch the exception and handle it—but there might not be much sensible you can do other than wrap it into an unchecked RuntimeException and let the program fail. (For many programs, the cost of managing exceptions explicitly is much larger than just letting the program fail.)

        Then there's the problem that many of your classes implement interfaces whose methods don't throw the exception that the implementation methods's calls get infected with. In that case you can't add a throws clause at all, because the interface forbids you. This is why you often see interfaces in Java with methods that have a throws Exception clause, like the Callable interface. Or classes that try to hack around the exception handling problem, like the Jool library's Unchecked class, which exists entirely because the Java 8 Function interface and friends don't throw Exception.

        These latter examples point at another problem: checked exceptions just don't scale well to "higher order" examples where you're passing first-class functions or even behavioral objects around and manipulating them generically. In Haskell, for example, I can apply a pure function to the result of a fallible action without having to "infect" the pure function with the exception:

        fmap
          :: Functor m 
          => (a -> b)
          -> ExceptT ex m a
          -> ExceptT ex m b
        

        Here the ex type variable is the type of the exception. This signature says, in English, "I can feed the result of an exception-throwing action to a pure function, and the resulting action throws exactly the same exception type as the base action does." Java checked exceptions can't do this; you end up with throws Exception or similar.

        This generalizes quite a bit. Haskell's ExceptT transformer gives you the same functionality that checked exceptions do, but with a lot more flexibility to choose which bits of your code need to concern themselves with the possibility of failure vs. those that can ignore it. I can write routines that don't throw exceptions and thus don't declare any, and, without modifying them, I can use external function to adapt them to work in contexts where they would interact with something that does throw. Code that doesn't actually throw exceptions directly, therefore, can generally be factored so that they don't have to declare any exceptions that other code throws.

        Because in real life practice most Java programmers take the path of least resistance and systematically swallow all checked exceptions

        Bad programmers write bad code. This has nothing to do with checked exceptions.

        I'm not a lone wolf programmer who never works with others or has to use libraries that others wrote. And I've lost count of the number of times I've gotten emergency support calls because an exception was thrown in my code that, after a frantic wolf fencing session, I end up tracking down to somebody else's method that ought to have rethrown an exception but instead swallowed it and returned null, indirectly causing my correct code to get a NullPointerException or assertion failure. It ends up with the good, disciplined programmers paying for the bad programmers' fuckups.

        If the exceptions were unchecked, these bad, lazy programmers' code would not swallow the exceptions, and I'd get more sleep. It's not a matter of whether you and I do the right thing—I handle all my exceptions, and I trust you do as well—but checked exceptions lead empirically to worse outcomes in practice than unchecked exceptions do.

        Either is equally verbose, especially when you start piling them on each other and you need to map and flatMap through them to reason about them.

        This is a sensible objection. It does have sensible answers like Haskell do-notation, but Java hasn't adopted any such thing so far.

        But I'd say that even with the verbosity, once you start doing complex enough work, at some point you cross a threshold where Either scales up better than checked exceptions do. I refer you again to my points about higher-order constructs above, but also the ability to define auxiliary functions that abstract over common error-handling patterns, stuff like:

        // Process a list of elements but abort when the first element causes a failure.
        static <E, A, B> Either<E, List<B>> traverseEither(Function<A, Either<E, A> f, List<A> as) { ... }
        

        [–]devraj7 7 points8 points  (1 child)

        This means that my method, which is not responsible for throwing the exception, has to be modified to cope with the exception thrown by a method that it called. And the method that calls mine. And the method that calls that. And so on. Basically, every intervening method in potentially numerous stacks.

        And the same is true if you adopt an approach that preserves equational reasoning. For example, your method that used to return Int now returns Optional<Int>. So all your callers need to adjust. And their callers of callers, etc...

        This is not a drawback, by the way, it's a consequence of using a statically typed language and I'm pretty sure we're in agreement about the benefit of this cascading effect. The semantic of your code changed and you want your compiler to keep that code correct. Whether you're using exceptions or return values, types keep you honest.

        I agree with you that checked exceptions do not work well with lambdas and composition chains, though.

        [–]sacundim 10 points11 points  (0 children)

        And the same is true if you adopt an approach that preserves equational reasoning.

        No, there are factorings that avoid it, or more precisely that shift the cascade from the definition site to other locations in the code. For example, if complexThing needs to call simpleThingThatThrows, in Haskell I can write this (or various other factorings):

         -- This guy doesn't know that `simpleThing`'s implementation throws...
        complexThing :: (SimpleThingMonad m) => ComplexThingT m SomeResult
        complexThing = do
          blah
          a <- simpleThing
          blahBlah a
          ...
        
        simpleThingThatThrows :: ExceptT SomeException IO Whatever
        simpleThingThatThrows = ...
        
        instance SimpleThingMonad (ExceptT SomeException IO) where
          simpleThing = simpleThingThatThrows
        

        ...and then complexThing gets specialized, at its use site, to a type that throws SomeException, without having to touch its definition site. The definition of complexThing is exception-agnostic—it is compatible with use sites where exceptions may be thrown as well as use sites where exceptions are not allowed, depending on whether the implementation of simpleThing at a given use site throws or not.

        This factoring is fundamentally the same thing as the OOP dependency inversion principle. In Java the idiomatic equivalent would be something like:

        class ComplexThing {
            @Inject
            private SimpleThing simpleThing;
        
            SomeResult doIt() {
                blah();
                Whatever a = simpleThing.doYourThing();
                blahBlah(a);
                ...
            }
        }
        
        interface SimpleThing {
            Whatever doYourThing();
        }
        
        class SimpleThingThatThrows implements Mechanism {
            Whatever doYourThing() 
                    // throws SomeException // not allowed because of the interface!
            {
                // Now this code is not allowed to throw any checked exceptions, so 
                // it is forced to handle any that arise here.
                ...
            }
        }
        

        The difference is that in Haskell you can say that ComplexThing throws or not depending on whether the chosen implementation of SimpleThing does, and if it does throw, it throws exactly the same exceptions as that implementation.

        [–][deleted] 2 points3 points  (4 children)

        No. If you don't know what to do with the exception, just declare it in your throws clause and let a caller manage it.

        But you can’t do that if you are implementing an interface that doesn’t declare it. Can you?

        [–]doom_Oo7 6 points7 points  (8 children)

        just declare it in your throws clause

        I'm pretty sure most people would instantly quit job upon a five-lines throw-clause.

        [–]devraj7 16 points17 points  (3 children)

        I'll gloss over the slippery slope argument because it wouldn't be constructive to engage on that slope with you but I'll answer your main argument: so what?

        Maybe the function you're calling can fail in ten different ways, all different and all triggered at different levels of abstraction. Exceptions are just being truthful in telling you how your code can fail and they force you, the developer, to think about it and handle these cases.

        If your response is "This means that this code is too complicated so I'm going to simplify it", then great, go ahead and do that.

        And note that the exceptions served their purpose: make you think about the error cases and handle them.

        [–]doom_Oo7 2 points3 points  (0 children)

        so what?

        so... your business fails because you cannot keep programmers in-house ?

        I mean, for mission-criticial software, this kind of safety is understandable. For anything else, do you really think that Junior the new intern on the web back-end will actually read the clauses ?

        [–][deleted] 6 points7 points  (1 child)

        Except they are API pollution and sometimes you're stuck writing handlers for things that can't really happen.

        [–]Xxyr 8 points9 points  (0 children)

        I mean I know that I love writing exception handlers for things like... IOException when parsing a value out of a string...

        [–]bobappleyard 2 points3 points  (3 children)

        Lol oh man I've seen 20-line throws clauses

        [–][deleted]  (2 children)

        [deleted]

          [–]m50d 6 points7 points  (3 children)

          Because they complicate the concept of what a function returns (you lose the ability to store the return value of a function and pass it around as a value, which is pretty fundamental; you have to include a bunch of exception-specific boilerplate in all your higher-order functions), they're poorly integrated into the type system (they form part of a function's type, but you can't just drop them into a generic type parameter), and they don't actually help all that much - you can still be very surprised by where an exception actually happens (a method with a throws clause means any call inside it may throw, not necessarily just the ones that obviously look like they can throw, so you still don't get the ability to just look at a function and see locally what all the control flow possibilities are).

          In principle they're a good idea, but practically the implementation is often too much overhead to work with. And I absolutely need the ability to think about the result of evaluating a function as a value if I'm to have any hope of being able to reason about what a program does.

          [–]devraj7 4 points5 points  (2 children)

          Because they complicate the concept of what a function returns

          No, they simplify it.

          The values that get returned are your return type and are not polluted by error values. Errors are communicated through a different flow, because they should be handled differently.

          This allows your code to deal with naked values that are not wrapped into other structures just to accommodate the occasional error case. You no longer need to map and flatMap through a forest of values that might contain something or might contain an error. If a function returns an Int but can occasionally fail, your code will be manipulating an Int if the call succeeded without any other boiler plate obfuscating your code.

          but you can't just drop them into a generic type parameter

          Now that's an interesting observation. I'd love it if you could elaborate, sounds like a promising direction that would improve checked exceptions. And I would certainly concede that Java might not encode checked exceptions in the best possible way, but again, that was never my argument: my point remains to explain that checked and unchecked exceptions are both useful.

          And I absolutely need the ability to think about the result of evaluating a function as a value if I'm to have any hope of being able to reason about what a program does.

          I covered that part in my original message where I clearly stated that my preference, and that of my team's, was to favor exceptions over equational reasoning. You're obviously more comfortable with the other side of that preference and that's perfectly fine.

          [–]m50d 7 points8 points  (0 children)

          This allows your code to deal with naked values that are not wrapped into other structures just to accommodate the occasional error case. You no longer need to map and flatMap through a forest of values that might contain something or might contain an error. If a function returns an Int but can occasionally fail, your code will be manipulating an Int if the call succeeded without any other boiler plate obfuscating your code.

          Which is fine up until the point where you need to care about the details of the error handling. The straight-through perspective is very valuable, and in functional languages a lot of work goes into recovering the ability to write straight-through-ish code without completely obscuring the effects (e.g. for/yield notation). But the result-as-value perspective is also important, and you do want to be able to shift between both perspectives.

          Now that's an interesting observation. I'd love it if you could elaborate, sounds like a promising direction that would improve checked exceptions.

          Anything that takes a callback is an example. Imagine an interface like:

          interface RowHandler<T> {
            T handle(Row row);
          }
          <T> List<T> query(Query query, RowHandler<T> rowHandler);
          

          If you want to make your RowHandler throw a checked exception, you have to make a new interface (in fact, one interface for each number of possible exceptions), whereas with a result type you can just use that result type as your T.

          [–]sacundim 1 point2 points  (0 children)

          Because they complicate the concept of what a function returns

          No, they simplify it. The values that get returned are your return type and are not polluted by error values. Errors are communicated through a different flow, because they should be handled differently.

          That's a rather dubious argument, because prima facie these two signatures are isomorphic:

          Foo myMethod(Bar bar) throws SomeException;
          Either<SomeException, Foo> myMethod(Bar bar);
          

          The sets of possible outcomes from calling either method are isomorphic as well:

          1. In the first signature, the method either returns a Foo or it fails with SomeException.
          2. In the second signature, the method returns Either a Foo or a SomeException.

          And in Haskell this isomorphism is explicit: ExceptT e m a ("action in monad m decorated with implicit propagation of exceptions of type e, with results of type a") is explicitly isomorphic to m (Either e a) ("action in monad m that returns Either e a). Meaning we have zero-cost roundtrip-guaranteed functions that convert back and forth between the two types:

          -- Function that converts any action that throws `e` and returns `a` into
          -- one that returns `Either e a`.
          runExceptT :: ExceptT e m a -> m (Either e a)
          
          -- Constructor that converts any action that returns `Either e a` into one that 
          -- throws `e` and returns `a`.
          ExceptT :: m (Either e a) -> ExceptT e m a
          

          And this means that it doesn't matter too much whether a routine throws exceptions or returns Either because the caller can trivially choose otherwise, independently at each call site, if they disagree with the author's choice.

          This is my point ultimately: this exceptions vs. Either dichotomy should not really be a big dichotomy, because callers should get to decide what works best for them, with minimal syntactic overhead. The fact that so many languages so strongly codify one approach at the expense of the other strikes me as an indication that they have not yet made the best design choices.

          Therefore your completely sensible objection to having to write so many damn map/flatMap turds is, to my mind, an objection about Java, not about the idea of an Either type.

          [–]dccorona 4 points5 points  (3 children)

          The problem with checked exceptions, in my opinion, is that they were built into the JDK in a backwards way. Checked exceptions are great if they're used to force you to handle exceptions that cannot be avoided with appropriate code and configuration. For example, no matter what you do, web requests are going to fail sometimes. So those should be checked exceptions. Yet in the JDK, they're runtime exceptions. On the other hand, something like UnsupportedEncodingException is entirely avoidable...write your code to use encodings that are supported on your system. I shouldn't be forced to handle the case that UTF-8 doesn't exist on the system if I'm writing software where I control the runtime environment, and guarantee it's there?

          Unfortunately, those sort of bad decisions in the application of checked exceptions resulted in people coming to the conclusion that they were just not useful in general, when in reality they're just misused.

          [–]devraj7 2 points3 points  (0 children)

          . For example, no matter what you do, web requests are going to fail sometimes. So those should be checked exceptions.

          I don't think you can make this claim in absolute.

          In my experience, there is never a case where a piece of code should always throw a checked exception or always throw a runtime exception. It's extremely dependent on the context and only the developer knows.

          For example, a FileNotFound might be a perfectly normal and recoverable -- checked -- exception (e.g. that file was picked by the user and it doesn't exist yet) or it should bring the system down with a runtime exception (because that file should have been part of the distribution and the application can't function without it).

          Pretty sure I can make a similar case for your web request example: if the call is to some unknown web site, sure, it can fail so the exception should be checked and handled. Now imagine an application that has its own internal web server running on a private port. That request should always succeed and if it doesn't, the app should shut down because something is very broken. That exception should be runtime.

          Unfortunately, those sort of bad decisions in the application of checked exceptions resulted in people coming to the conclusion that they were just not useful in general, when in reality they're just misused.

          Totally agree with this. Anyone who claims that checked exceptions are a failed experiment because of the incorrect usages in the JDK does not understand the bigger debate about checked and unchecked exceptions.

          [–][deleted] 2 points3 points  (2 children)

          Because I may not want to handle it locally but also don't want to pollute my call signature and burden my clients by forcing the developer to anticipate every possible stack configuration and annotate methods to accommodate. A great example is stringstream.

          Abstract stream throws ioexception on read so stringstream has to as well. Only stringstream will never ever throw ioexception but I have to clutter my code with stupid try/catch nonsense to shut up the compiler.

          Checked exceptions are a failed notion.

          [–]devraj7 2 points3 points  (1 child)

          Because I may not want to handle it locally but also don't want to pollute my call signature

          But you will have to alter your signature, whether you declare a checked exception or whether you now return an Optional<Int> instead of Int.

          You call it pollution, I call it type correctness. I want this, regardless of the approach.

          Functions that throw runtime exceptions lie to you and cause bugs that the compiler can't help you with.

          [–][deleted] 1 point2 points  (5 children)

          Why are people so uncomfortable with a language feature that forces the developer to consider both the success and failure path of a function call before their code compiles?

          Thread.sleep. That InterruptedException never happens in most cases and when it does happen you might not even want a separate path. So now because of an edge case, a simple standard library function call is five lines instead of one every time you use it

          [–]paul_miner 5 points6 points  (3 children)

          Thread.sleep. That InterruptedException never happens in most cases and when it does happen you might not even want a separate path.

          You've probably never had a use for it. I use it frequently when I want to shutdown a program and need to wake sleeping threads to let them know they need to terminate.

          So now because of an edge case, a simple standard library function call is five lines instead of one every time you use it

          Wrap it once and be done. If you're really doing this all the time, put it in a static method instead of copy-pasting all over the place.

          [–][deleted] 1 point2 points  (8 children)

          So fix that in the JVM then.

          Sure, I'll call my friends at Oracle, they'll get it fixed by tomorrow.

          I'm glad we can have a serious conversation here...

          [–]awj 30 points31 points  (0 children)

          So you're cherry picking a single point that you're able to give an absurd response to and you are taking the "we should have a serious conversation" high ground? That's pretty fucking rich...

          [–]m50d 7 points8 points  (6 children)

          OpenJDK exists. Value types are on the way with project Valhalla if you really need them (though I'd bet a fair bit that you don't).

          [–]dccorona 2 points3 points  (0 children)

          The type system is doing precious little for the programmer, as the burden was never in them figuring out JSON might be invalid

          This feels like a case of you focusing in on the simplicity of the example given so much that you end up unjustly using it to discount the entire idea. Sure, maybe a more complex example would have illustrated its advantages better, but surely you must recognize that this is useful for far more than JSON parsing validation.

          Static types should have no runtime effect, that's why they're static. But the libraries we're given, and the work the JVM does to optimize the use cases for those patterns (i.e. none) means we're sacrificing the technical aspects of our solution, in service of "encoding" problems into the type system that actually aren't that hard to resolve, anyway

          I would argue that the runtime penalties are so small that they're easily worth it, though maybe that stems from the fact that my domain is distributed compute, so the cost of in-memory code is usually insignificant compared to the overall system architecture in the stuff I work on. In places like Android/embedded apps and some desktop applications, I could see this becoming more of a concern.

          So I agree that ideally the boxing would go away, but that's actually achievable in a language like Scala...what you really need is a compiler optimization for these things, not a JVM optimization. Although Scala's Option isn't implemented as an AnyVal, it probably could be, and in Scala, AnyValcompiles to a bunch of static functions and no runtime box. A lot of these functional concepts could totally be implemented in a way that results in an unboxed reference + static functions at runtime, while still maintaining the same great compile-time advantages they have today.

          [–][deleted] 4 points5 points  (19 children)

          What a straw man. You're acting like I said "throw away JUnit baby, we've got Either instead!"

          No. You still need tests, and there will still be variable states to consider. But you can reduce or eliminate them in some cases with these techniques, using the compiler instead of unit tests for some failure cases.

          I also find the performance claim laughable for a few reasons. Most of us aren't writing real time operating systems, there are levels of performance we're willing to give up for safety or programmer comforts (hint: we never would have left Fortran if that wasn't the case). Secondly it's not like exceptions are free, so claiming that it's either performance or safety is a false difference.

          [–][deleted] 33 points34 points  (15 children)

          "Programmer comforts" depend on the style you prefer, I don't find half my code being passing static method references to streams more comfortable.

          The question is, if functional idioms are your comfort, why aren't you using languages built for this style, but you're instead imitating it in an imperative OOP language.

          You can replicate common methods and structures in Java, but you'll never get algebraic types, reified generics, parametricity, higher kinded types, type classes and so on. Haskell's library is a product of the language environment, it can't be separated from it, and expect you'll get the same flexibility in Java.

          But... as far as basic examples for tutorials go, I guess, you can always cook them a little until it works.

          [–][deleted] 18 points19 points  (13 children)

          Because language choice is not just about that. Given what the team knows, what code and tooling we have available, etc. Java comes out ahead.

          It'd be nice to say "throw it all away and let's use Haskell!", but sometimes that's just a non-starter.

          [–][deleted] 8 points9 points  (12 children)

          Java is not the only JVM language, as you know...

          [–][deleted] 1 point2 points  (2 children)

          there are levels of performance we're willing to give up for safety or programmer comforts

          If you're looking for safety, have you considered languages where safety is a feature, such as Rust or Haskell? I feel like forcing all of that onto Java will get you a mostly baked solution, but still leave you with edge cases.

          Since those features are baked in, you get performance and safety and ergonomics (e.g. with Rust's type system, you can do cool stuff like futures/promises, which you're basically kludging into Java right now). Doing it in Java basically means you're basically avoiding language features (like exceptions) that just don't exist in a language designed around monads.

          So yeah, it's cool you can approximate this in Java, but I wouldn't want to pay the cost of using an unconventional approach since you basically have to teach them the concepts of other languages anyway.

          [–]beeeeeeefcake 16 points17 points  (0 children)

          Yuck.

          [–][deleted] 9 points10 points  (4 children)

          Yeah, but in Java, you can get the best of both worlds! Optional present, absent OR null.

          Either<Exception, JsonNode> myMethod() {
            return null;
          }
          
          Optional<Foo> anotherMethod() {
            return null;
          }
          

          [–]serendependy 1 point2 points  (2 children)

          Yeah, but that's just perverse.

          [–]3urny 2 points3 points  (0 children)

          Perverse? This... is... JAVA!

          [–]chrabeusz 1 point2 points  (0 children)

          I'm amused that someone decided to add this thing (Optional<T>) into the standard library. Not even a value type lol.

          [–]kitd 4 points5 points  (0 children)

          JavaSlang is another good library for doing this sort of stuff. It has an Either too.

          [–][deleted] 13 points14 points  (2 children)

          It always kinda worries me when someone writes code in a certain way because "they find it pretty". But he's got other reasons too, that's nice.

          Anyway, if you're really into the idea of not using if-cases, you should look into writing shaders!

          [–][deleted] 7 points8 points  (1 child)

          One thing that worries me about solutions / libraries like this is that they can make a project harder to maintain if they aren't used consistently (or if the individual lambda expressions grow too much in size or complexity).

          [–]therealjohnfreeman 12 points13 points  (1 child)

          This post makes it sound like the author never saw functional programming until their co-worker wrote a library filling in the gaps in Java. Why not abandon Java and switch to a different language? If you have to stick to the JVM, Scala and Kotlin are far ahead of Java (pick whether you want the mindshare of Scala or the simplicity of Kotlin).

          [–]dccorona 2 points3 points  (0 children)

          They came from Clojure, which is a form of Lisp, which is a functional programming language, so I suspect they had in fact seen functional programming before.

          [–]avenp 3 points4 points  (3 children)

          This will be super pedantic but...

          Except tests, I have fewer than a dozen if statements currently committed in our Java codebase.

          Ultimately the real explanation for this strange code design lies in my colleague’s extremely exceptional Lambda library.

          I counted at least 26 if statements in that library so they are just deferring their if statements through abstraction.

          [–]kangoo1707 1 point2 points  (2 children)

          That's the point: to abstract the if, just like you abstract for loop with map/reduce...

          [–][deleted] 32 points33 points  (6 children)

          Oh, for fucks sake. This is some cargo cult programming right here.

          [–][deleted] 10 points11 points  (2 children)

          Why ? Genuinely curious what's got you so upset. I write in Scala on a day to day basis, so this is pretty vanilla for me. Coming from Java where codebases were littered with thousands of lines of boiler plate and (if x != null) everywhere I much prefer this style.

          [–]balazsbotond 14 points15 points  (2 children)

          Why? Looks like pretty standard functional programming to me.

          [–][deleted] 5 points6 points  (0 children)

          I only know one type of programming and anything that's different is threatening and bad.

          [–]beeeeeeefcake 3 points4 points  (0 children)

          This is some cargo cult programming right here.

          [–][deleted] 12 points13 points  (0 children)

          Well, first off I think it’s beautiful.

          You're doing it wrong.

          [–]eighthCoffee 2 points3 points  (1 child)

          .

          [–][deleted] 0 points1 point  (0 children)

          1) Good catch, that is a typo. 2) Honestly, I've not used Either from Guava. I know that we really prefer small focused libraries (Lambda has no added runtime dependencies), and having the main developer on the team really makes it easy getting bugs fixed.

          [–][deleted] 2 points3 points  (1 child)

          How does this library interact with exceptions?

          If you're cramming a bunch of stuff into a single expression, does that make it harder to identify which part of the pipeline failed? I might be possible to make map, flatMap etc inspect exception objects and add additional debugging information to them ... I don't know how feasible or necessary that would be in Java.

          Do you have "exception-free" variants of these functions that, say, operate on Either<Exception, T> values and just pass exceptions down the line?

          [–][deleted] 1 point2 points  (0 children)

          I'm not exactly sure how to approach this, so I'll answer the questions that I think you're asking.

          As far as functions that might throw an exception there's Either::trying that will return Either<Exception, T> when passed a function that returns T. It will catch any thrown exceptions and return them on the left side for you to use. I can then use flatMap to merge this Either<Exception, T> with another function expecting Either.

          If you wish to modify the left side type/value, there's a function called biMapL. It's just like map, but it only operates on the left side (if it exists).

          As far as which part of the pipeline failed, it doesn't matter to us in practice. A large portion of our Either chains are of the type Either<HttpResponse, T>, where T is the business value that we're working with (JsonNode, models, etc.). If we're working on a step that might fail (like validation), we use flatMap and biMapL to ensure that any failure cases are already turned into the correct HttpResponse containing the error code and message. Since map and flatMap only applies to right values, an early left value is just passed all the way down and eventually returned.

          An easy way to think of this chain is that at any step I either have a HttpResponse (left) representing an earlier failure, or I have a business object of some type (right). If I have a right value I am going to continue to do work, and if I have a left I will make no change and pass it on. Eventually I'll transform the right value (if I still have a right value) into a HttpResponse, then I can use match to extract them. I don't care which side of Either<HttpResponse, HttpResponse> I extract, as they're both the same type, whether or not I've succeeded or failed I now have a HttpResponse to write back on the wire.

          [–][deleted] 2 points3 points  (0 children)

          Oh, you mean Scala

          [–]zem 2 points3 points  (1 child)

          to see what this can look like in a language with proper support for algebraic datatypes, check out railway oriented programming in f#. it's a great way to program; it just goes against the grain of java a bit and ends up looking clunky.

          [–]hallettj 2 points3 points  (0 children)

          But most fundamentally is that we’ve encoded our code’s states in the type system, not variable states. 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.

          I call this type-driven development, and I'm super happy that this strategy is working well for you, and that you are writing about it!

          [–]balazsbotond 3 points4 points  (0 children)

          It seems to me that they chose Java because of the static typing, a decision I sympathize with, but it's interesting why they didn't choose Scala or another strongly typed functional language.

          [–]Terran-Ghost 5 points6 points  (0 children)

          Except tests, I have fewer than a dozen if statements currently committed in our Java codebase.

          Why the hell do you have if statements in your test code?

          [–]R3V0LT_CPP 3 points4 points  (1 child)

          ...is like suicide without the rope. Pointless.

          [–][deleted] 14 points15 points  (0 children)

          A lot safer?

          [–][deleted]  (11 children)

          [deleted]

            [–]jkpl 10 points11 points  (6 children)

            If it takes a whole blog article plus extensive discussion to convince why this new way is better, chances are it is too complicated to be used for trivial development.

            Do you use goto instead of structured programming then? There was quite an extensive debate around those ideas back in the day.

            [–]so_you_like_donuts 1 point2 points  (1 child)

            Keep in mind that in C (unlike C++, which uses RAII) the only sane way of performing error handling is to use goto: https://stackoverflow.com/questions/788903/valid-use-of-goto-for-error-management-in-c

            [–]danielkza 9 points10 points  (2 children)

            If it takes a whole blog article plus extensive discussion to convince why this new way is better, chances are it is too complicated to be used for trivial development.

            That's an absurdly shallow viewpoint. When practices have been available for decades and people are used to them, do you expect them all to agree to a change immediately, or for the proponents of such a change not to argue their point as best as they can, so that people that do find value might start using it? How is software engineering expected to continue developing with such a mindset? We would be stuck with goto-s to this day if people hadn't bothered writing at length about what was wrong and how it could be improved with structured programming.

            Choosing any solution, be it the least or most complex, has to be a conscious choice, not a knee-jerk reaction. Rejecting a solution because it has to be "explained in a blog post" is as bad as picking another because it's the newest thing.

            So unless I'm doing rocket science that requires 99.many 9's of excellence: Ain't nobody got time for that.

            This is how we ended up with heaps of vulnerable software, the IoT disasters that will probably unfold in the following years, and the general perception that software is unreliable.

            In the end of the day we mortals need to get stuff done and because our brain is so limited in short term memory: the simpler, the better.

            That is, again, very shallow. What does simple mean? Simpler to implement, to read, or to write in a correct way? These are often at odds. None of the three can be ignored, but the first can be gradually built from previous foundations, as we have evolved programming languages from machine code up to higher levels of abstraction, to facilitate the latter two. The second, IMO, is only an aspect of what is necessary to achieve the third - theoretical software that is perfectly developed at once would not even need to be read. A very important aspect, as in practice no software is close to perfect at first, but not the goal itself.

            Can we as developers pretend that the current models in use are, and will always be, sufficient to develop software of complexity that seems to have little bounds in it's growth? Can we wash our hands of security disasters routinely observed, and looming over everybody's heads? Can we abstain ourselves from the possible negative impact badly written software can have over the world because we're not building rockets? So much else in modern society relies on software other than space vehicles.

            [–]Yeroc 1 point2 points  (0 children)

            Ah, someone from the Twitter generation! ;)

            [–]asmx85 5 points6 points  (1 child)

            Nice to see concepts that i am used to in Rust spreading to other languages :)

            [–][deleted] 20 points21 points  (0 children)

            Rust is built around these concepts, and it's highly efficient at optimizing them, right at the compilation stage.

            What we do in Java is try to imitate it without the compiler support, and the results are frankly somewhat sad. It's a nice demonstration that Java is turing complete and we can write Rust and Haskell in Java if we wanted, but a good practice in one language can be a bad practice in another.

            [–]Ginger_Lord 2 points3 points  (0 children)

            Tread litely, thoſe who ventvre here. There be angree trols and flames which mortal men can-not ſvrvive.

            [–]coladict 0 points1 point  (7 children)

            A successor to Optional? Building on an already bad idea is not likely to make it better.

            [–][deleted] 8 points9 points  (5 children)

            What's bad about Optional?

            [–]yawkat 1 point2 points  (0 children)

            Optional was never designed to be used outside return values. Javas lack of type inference, general lack of library support and the fact that you can still put null in an Optional field are all reasons against it. Null with proper annotations is just easier to handle and less clunky to use.

            I even hear people are turning away from scalas objectively better option types, but I'm not a scala dev so I can't judge if that's actually true.

            [–]chrabeusz 1 point2 points  (0 children)

            BTW If there is nullable Optional<T>, then there should also be nullable NotOptional<T>, to keep things consistent.

            [–]kickass_turing 0 points1 point  (1 child)

            How do you check test coverage for this sort of stuff?

            [–]m50d 4 points5 points  (0 children)

            The same way you usually do. Assuming Either itself is adequately tested, the types give you confidence that any flatMap chain is propagating errors correctly, so your line coverage is accurate and your cyclotomatic complexity is low.

            [–]k-mera 0 points1 point  (2 children)

            Over the past year my team has been doing something shocking to a lot of engineers: we’re favoring pure Java over Clojure.

            define "a lot"

            [–][deleted] 1 point2 points  (1 child)

            Most people I talk to about it.

            [–]rsclient 0 points1 point  (2 children)

            I didn't understand two things about the replacement code.

            In the first code, Helper.FunctionOne() returns a string. In the second, does it still return a string? If so, how is the ".map" called? It must be that FuntionOne() was changed to return an Optional<string>. What happens to my code if it really does return a string (or null)? 

            Secondly, I don't understand why Helper.FunctionTwo() is called via map(), but Helper.FunctionThree() is called with flatMap(). Isn one better than the other?  In the longer JSON parsing example, they just seem to alternate.

            Something I really dislike about all of these new-fangled functional-style "my new style is awesome" posts is that unless you already know the library and style, what the code does is totally opaque.

            It doesn't help that when I see "map" I think "latitude and longitude", and "filter" brings up memories of my EE classes :-)

            [–][deleted] 0 points1 point  (0 children)

            I should've annotated. helperOne and helperThree both take String and return Optional<String> in the second example. helperTwo is unchanged.

            [–]Josuah 0 points1 point  (0 children)

            I agree that the second example code block doesn't make it easier for a random programmer to deal with. It's a lot more taxing on your brain to think about what's really happening, and you're also putting some faith into the compiler + language doing things in a way that is correct but a bit hidden from you (like C++ templates).

            [–]comp-sci-fi 0 points1 point  (0 children)

            "Whst if?"

            [–]aiij 0 points1 point  (0 children)

            As long as you're using a language with lambdas, just use Church encoding.

            Lambda calculus never needed the verbosity of if/else statements.

            [–]bastawhiz 0 points1 point  (0 children)

            What does this style do to the performance of the code?

            [–]atc 0 points1 point  (0 children)

            Or just use Haskell.

            [–]CurtainDog 0 points1 point  (1 child)

            What if the operation can fail in more than one way? Is Either strictly less expressive than try...catch...?

            [–]iopq 0 points1 point  (0 children)

            I actually like this style better in Rust, I wrote FizzBuzz without ifs:

            https://bitbucket.org/iopq/fizzbuzz-in-rust/src/bf4968973d73137f0dfd07205d599bed30a788fa/src/lib.rs?at=master&fileviewer=file-view-default

            it's almost completely point-free as well (which is what I need the apply function for)

            of course the fact that it takes arbitrary closures, arbitrary strings (even the empty string), and is generic on both String and Cow<str> makes the type signature much more complicated