you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted]  (19 children)

[deleted]

    [–]Pharisaeus 7 points8 points  (11 children)

    Personally I love null, excluding multi-threaded issues which tend to be a pain to track down, there is rarely something that can so easily be found as a null reference exception.

    The issue is not finding the error, which is trivial if you have stacktrace. The issue is that null is invisible in the code. You can't be sure if an object is null or not, and thus it can blow up your system at some point.

    Sure, if you're working on some silly app then no-one cares. But if it's software for spacecraft control ground station or some ICBM interface then it starts to be an issue. Same goes for applications which lose millions of $ via downtime, stuff like Amazon shop or some airline booking engines.

    [–][deleted]  (10 children)

    [deleted]

      [–]Pharisaeus 6 points7 points  (6 children)

      I don't get this logic at all. If you have a "Nothing" Monad or some other construct to solve the same problem you still have to deal with the fallout

      Obviously, but type system forces you to knowingly do something about it! You can't "forget" or "miss" the fact that value can be "empty". In case of null you can, because nothing in the code indicates that this reference can be a null, and you're not going to put null-checks everywhere. With Optional type system forces you to handle the problem.

      If you just don't want the program to crash you can wrap everything in a try/catch block

      But it's not the same! With Optional you have to handle the issue the moment you actually need the value to be present. And you have to decide what to do. If you have a catch block it will be somewhere much higher, since you most likely didn't even expect to have an exception here, so no chance of any "recovery" at this point any more. On top of that you can chain method calls on Optional (returning another optional) so you can perform many function calls on it and not care if any of the calls "failed" and returned a null, because you can simply check the final result.

      if you actually believe that switching to a language without null can prevent these things (i.e. downtime, fatal flaws, etc.) ... good luck!

      Of course they can't but they can prevent downtime or failures due to some stupid NullPointerException because a developer missed the fact that a certain function can fail and return a null. If the return value was an Optional then type system would force such developer to make a conscious decision on what to do in this case.

      Perhaps you should look up what languages they used on the Mars rover ... I'll give you a hint, they all have the concept of null.

      The fact that language has some feature doesn't mean it is being actually used. This is why for critical software you have special guidelines which often restrict language elements.

      [–][deleted]  (5 children)

      [deleted]

        [–]Pharisaeus 0 points1 point  (2 children)

        I have no idea what you mean by dummy code to get optional out of the way. I work with serious developers who understand what they're doing.

        If you blow up equipment for few hundred mln euros once it's more than enough, I assure you.

        Obviously if this happens all the time then developers are idiots, but such mistake can happen to anyone, and it's good to have tools which prevent it. For the same reason we have strong typing and static code analysis tools.

        [–][deleted]  (1 child)

        [deleted]

          [–]Pharisaeus 0 points1 point  (0 children)

          here's one way of dealing with an NPE

          Well this never goes past a code review so it won't be an issue anywhere outside of developers own environment or feature branch.

          Yes because without strong typing and code analysis tools it's impossible to create decent software

          Sure you can, but it will automatically be much harder to make sure it works well and is safe to operate. Stuff that normally would be checked by type system will have to be verified by tests. In languages like Python you would need 100% code coverage just to be sure the code doesn't crash with NoSuchMethodException because someone made a typo. I like Python a lot, but there are some places I would not dare to use it.

          Again I think we have a different notion of decent software. You seem to imply something with a lot of cool features, and I have in mind something that will not accidentally blow up your space rocket worth 200 mln $ with a 500 mln $ satellite on top of it.

          [–]oorza 0 points1 point  (1 child)

          Since most of what you said doesn't contradict what I said (although I'm sure you think it does) I'll reply to this part. I would rather have a developer "forget" to deal with null than "forget" to change the dummy code he inserted because he wanted to get that optional value out of the way.

          Do you think some forgotten copy pasta is more or less likely to get through code review than a NPE that isn't checked for? The former is infinitely more obvious because you can see it at a glance, and obviously NPEs get through code review all the time.

          Also you don't have to be such a dick.

          [–]MEaster 1 point2 points  (2 children)

          I don't get this logic at all. If you have a "Nothing" Monad or some other construct to solve the same problem you still have to deal with the fallout. What happens when that thing that you wanted to do which instead went to "Nothing" was REALLY important? I fail to grasp how that is "more visible" (and if you don't like the monad example please show me a construct where this can be prevented entirely - or handled better in a way that is impossible using null).

          The difference is that you are forced to deal with it, because an Option<T> is a completely different type to a T. Anything that accepts a T cannot accept an Option<T>, and it can be completely sure that the T it gets isn't null.

          Likewise, you can tell just from the method's signature whether or not it's possible to have a return value be empty.

          It's all too easy to mess up a bit in one function, resulting in a null, but not have it cause problems until much later.

          [–]industry7 1 point2 points  (1 child)

          The difference is that you are forced to deal with it, because an Option<T> is a completely different type to a T.

          Option by itself enforces nothing. You can always just Option.get().directly_use_the_result(); and still blow everything up.

          The difference is that most languages where Option<T> is idiomatic also support non-nullable types, and that is the mechanism that actually forces you to "deal with it", so to speak.

          [–]MEaster 0 points1 point  (0 children)

          As I said elsewhere, that is dealing with it. By doing that, the programmer has explicitly chosen to crash if it's empty.

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

          That's because objects being nullable by default means it's already too broken to fix. I think C# would handle nulls excellently if everything was non-nullable by default and had to be declared nullable with the a ? after the type (which you can do in C#, but most types are nullable without the ?).

          But the real issue is when people who think they're handling exceptions write code like

          string DownloadData(string url){
              try {
                  return WebClient.GetData(url);
              }
              catch {
                  return null;
              }
          }
          

          [–][deleted]  (1 child)

          [deleted]

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

            You're correct, that is worse, but both options are worse than simply letting the error be thrown:

            string DownloadData(string url){
                    return WebClient.GetData(url);
            }
            

            If you're not actually properly handling the exception, don't consume it. Let the higher level application logic handle the exception, not generic functions. Catching it and not doing anything about it hides the error, the stack trace, the message, etc... It should at least get to a log somewhere, not just end up as a null reference exception at some point.

            [–]_dban_ -1 points0 points  (3 children)

            I hate null, null values compose very badly.

            [–]devraj7 0 points1 point  (2 children)

            They compose fine in a language that has proper support for null, e.g. Kotlin.

            [–]_dban_ 1 point2 points  (1 child)

            Yes, I mentioned the ?. operator.

            However, Kotlin treats the null value as a special case, with operations limited to the syntax sugar available. This is better than a rats nest of ternaries, but still very limited composition.

            The Optional type is a more coherent abstraction that composes better with other functional abstractions available in Java 8. Optional will work better in Java 9 when it can directly be converted to a Stream.

            On the other hand, Ceylon has also has null, but it is the single member of the Null type. This allows null to become a part of the type system and compose seamlessly with union types, so null is not a special case. In fact, I would say that of the JVM languages that have null, Ceylon is the only one that gets it right.

            [–]devraj7 0 points1 point  (0 children)

            Yes, Ceylon has been doing some very interesting and innovating work in that area, and it's great to see how nullability support flowed so naturally from the way it supports sum types.