NULL: the worst mistake of computer science



  • @dkf said:

    Why does it matter?

    I don't know.

    I'm just showing that if you care so much about it, you can find out why in the same query.

    NULL isn't the problem.


  • Discourse touched me in a no-no place

    @xaade said:

    I'm just showing that if you care so much about it, you can find out why in the same query.

    Ah! The spirit of doing it because you can. Carry on there…



  • C "NULL is a problem because I can't tell in an outer join what it means"

    X Demonstrates that you can tell

    X Therefore, it's not NULL that's the problem. You're creating the problem that you say there is no solution to.


  • Discourse touched me in a no-no place

    Those more into strict relational algebra than I am would probably beg to differ. I'm nowhere near as dogmatic when it comes to SQL. 😃 I just want the DB schema to be at least somewhat sensible for both the data and the queries over that data…



  • @PleegWat said:

    That's not where it's annoying. Where it's annoying is:

    bool x = some_external_value;
    int y;
    
    if( x )
    {
        y = 42;
    }
    
    /* some code which does not touch x or y */
    
    if( x )
    {
        printf( "%d\n", y );
    }
    ```</blockquote>
    
    At least Visual Studio does recognize that `y` may not be initialized in some cases and will warn you about it in the editor.

  • Java Dev

    @Rhywden said:

    At least Visual Studio does recognize that y may not be initialized in some cases and will warn you about it in the editor.

    So will GCC on compile, which is my usual target. And I do prefer 'may be used uninitialized' over silently assuming it will be initialized, which you sometimes get when an uninitialized variable's address is passed to a function.


  • Discourse touched me in a no-no place

    Is it just me, or does Dicksucks claim there's an exception every time this thread is loaded (before loading it anyway) for others too?



  • Discuss did that to me several hours ago when the pages loaded slowly. Now everything looks okay. Too okay.



  • @PWolff said:

    Too okay.

    Don't worry. Disgorge will fix that.



  • @loose said:

    > The reciprocal of Zero is a number that is not a number. And that's a problem

    Filed under: 🍿

    ftfy

    However, it's also not not a number. Don't believe me?

    And don't forget about the reciprocal of negative zero.


  • Discourse touched me in a no-no place

    Remembering that discussion, I reckon that the actual worst mistake of computer science is teaching idiots who don't real the documentation (and so fail to understand what NULL and NaN mean) to program computers in the first place.



  • @PleegWat said:

    ```
    bool x = some_external_value;
    int y;

    if( x )
    {
    y = 42;
    }

    /* some code which does not touch x or y */

    if( x )
    {
    printf( "%d\n", y );
    }

    
    Code like this won't even compile in Java, and that's a language that everybody ridicules.


  • Good job Discourse, you fucked that up pretty good.


  • :belt_onion:

    I think you need to add a newline into the quote. Why? Who the hell knows...
    @another_sam said:

    @PleegWat said:
    ```
    bool x = some_external_value;
    int y;

    if( x )
    {
    y = 42;
    }

    /* some code which does not touch x or y */

    if( x )
    {
    printf( "%d\n", y );
    }

    
    Code like this won't even compile in Java, and that's a language that everybody ridicules.</blockquote>
    
    Yep.
    
    DiscoMarkdownHTMLBBCodeParser bugs. Still haven't been fixed, but at least <a href="https://what.thedailywtf.com/t/what-favorite-ui-element-will-we-lose-next-hopefully-not-flags/6755/428?u=sloosecannon">logout functionality was removed</a> (then re-added) in the latest version. So... progress is being made?


  • @antiquarian said:

    So having the error show up at compile time rather than run time has no value? :wtf:

    But it doesn't show up always, that's the issue. If you're not required to use Optional, the [Java] compiler (as it is now) won't tell you that you might be missing a null reference check somewhere.
    And even then, you can still fail at checking Optional and attempt to use it anyway, which will result in a run-time error. (I think it's something like NoSuchElementException.)

    As I already said (and @Captain mentioned below your reply), the value it has is making the programmer aware of the possibility of null. If the language is designed like this from the start (I think Rust does this, and I see in this thread that Haskell likely does too.), then it is obviously helpful. In java/C# - not so much.

    I'm not arguing that Optional/Maybe/... constructs are bad or useless, I'm just arguing that tacking them on as an afterthought doesn't help much, when there's no obligation to use them. Just creates further mess when some parts of the code use them and some don't.

    And, returning to my original point - if you abolish null from a language and then add something like Optional<T>, that's just a different name for null/nothing.



  • @dkf said:

    I reckon that the actual worst mistake of computer science is teaching idiots who don't real the documentation (and so fail to understand what NULL and NaN mean) to program computers in the first place.

    But anybody who wants to should be able to program. It must be so; Blakey said it.



  • @loose said:

    > NULL is a value that is not a value. And that’s a problem.

    Got as far as that and stopped reading /taking it seriously. Why?

    Because you're an ignoramus?

    @loose said:

    Zero is a number that is not a number. And that's a problem

    There we go!



  • Any entity that exists, from abstract concepts to useless bags of skin, by definition, has to have a value that can be described. Sometimes there is nothing other than NULL to provide an absolute and accurate description.

    By the way, are you aware that "zero" had to be invented in order to allow Math to be viable?


  • :belt_onion:

    @loose said:

    Any entity that exists, from abstract concepts to useless bags of skin, by definition, has to have a value that can be described. Sometimes there is nothing other than NULL to provide an absolute and accurate description.

    By the way, are you aware that "zero" had to be invented in order to allow Math to be viable?

    I use null frequently in cases that aren't exceptional but cause the method to fail. An obvious choice would be a method that gets the config values for a program but does not need those values to continue (fall back to default, etc). Yes, you may throw a FileNotFoundException, catch that, etc, but it is much simpler, in my mind, to return null and run a null check before using the result.
    null only bites you in the butt if you're a poor programmer who allows that null to escape its confined space into an area where it can cause problems. Using an IDE with static analysis (as discussed above) and @Nullable and @NotNull annotations are both immensely valuable tools in preventing these kinds of errors, and are usually readily available.
    Granted using null can lead to gotchas, but by being proactive, never assuming anything which isn't specified by the method contract, and being careful about program flow, you can ensure that null problems never really occur.



  • @loose said:

    Any entity that exists, from abstract concepts to useless bags of skin, by definition, has to have a value that can be described. Sometimes there is nothing other than NULL to provide an absolute and accurate description.

    The difference here is a type that is explicitly and obviously nullable, to prod the programmer into thinking about the potential null value, and other types that cannot ever be null so the programmer knows he does not have to think about them.

    @loose said:

    By the way, are you aware that "zero" had to be invented in order to allow Math to be viable?

    I am aware that zero was adopted as a better alternative to the null value when zero is what is actually meant. Zero wasn't invented to "allow Math to be viable", it was used as part of a place value system because the notation is really handy.


  • :belt_onion:

    @another_sam said:

    The difference here is a type that is explicitly and obviously nullable, to prod the programmer into thinking about the potential null value, and other types that cannot ever be null so the programmer knows he does not have to think about them.

    What happens when I have to cast one to an Object? (As is frequently required in Java programs. Benefits/downfalls of this may be discussed elsewhere).

    Note: I don't necessarily disagree with your idea, but language constraints and other issues make it infeasible to me.



  • @sloosecannon said:

    Yes, you may throw a FileNotFoundException, catch that, etc, but it is much simpler, in my mind, to return null and run a null check before using the result.

    mightBeNull.getOrElse(default)
    mightBeNull.map(notNullValue => dontCallThisIfValueIsNull(notNullValue))
    

    @sloosecannon said:

    What happens when I have to cast one to an Object? (As is frequently required in Java programs. Benefits/downfalls of this may be discussed elsewhere).

    Unfortunately the Java type system doesn't support not-nullable types. This also means Scala doesn't support them either, which sucks hard, because it includes a nice Option type. So if you cast to Object you're going to break stuff sometimes, which is what happens now anyway.

    @sloosecannon said:

    Note: I don't necessarily disagree with your idea, but language constraints and other issues make it infeasible to me.

    For existing languages the biggest problems are the APIs you need to call, because they're already established with null as a valid return type. Retro-fitting such APIs with Optional would be great but it's going to take a long time and the effect will only be of benefit when programmers see Optional everywhere and a null looks strange to them.



  • Raw:

    int x;
    if (very_difficult_problem(constant_input))
    {
    x = 42;
    }

    
    I dunno. Maybe it would work, but it would either require some insanely powerful static analysis tools, or the compiler to chew you off on missing cases that are impossible anyway.
    

    Output:

    WTF, DiscoBBHTMLMarkDownParser?


  • :belt_onion:

    How about searching a database for a query result and turning it into an object? Or another case where there is no default value?



  • Preview:

    Result:

    Where's my newline? FUCKING HELL I'M FINDING BUGS WHILE REPORTING BUGS.


  • :belt_onion:

    FUCKING HELL WHY DOES IT DELETE TWO CHARACTERS INSERT 0 NEWLINES WHEN I PRESSED BACKSPACE ENTER ONCE?



  • The point I have been making is:

    Null has to exist in order to describe something that does not exist. In the case of programming languages this is critical, otherwise you have the situation that "mathematicians* had before they invented the concept of zero - which is a very specific type of NULL. Resulting in a BSoD which helps nobody and nothingNULL.......

    1 + 1 = 2 😄
    1 - 1 = ????? :( :Wtf: "...run for the hills, it's the end of the world..."


  • :belt_onion:

    Alternate punchline: FUCKING HELL WHY DOES IT DELETE TWO CHARACTERSDO I GET TWO BUGS WHEN I PRESSED BACKSPACE ONCEONLY FOUND ONE



  • @sloosecannon said:

    Alternate punchline: FUCKING HELL WHY DOES IT DELETE TWO CHARACTERSDO I GET TWO BUGS WHEN I PRESSED BACKSPACE ONCEONLY FOUND ONE

    DiscourseDiscuss



  • @sloosecannon said:

    How about searching a database for a query result and turning it into an object?

    Return Option types in your record where those columns are nullable in the database.

    @sloosecannon said:

    Or another case where there is no default value?

    None is an Option that has no value and is more-or-less equivalent to the literal null.


  • :belt_onion:

    @another_sam said:

    Option types

    ?



  • @sloosecannon said:

    @another_sam said:
    Option types

    ?

    In Java, Optional, in Scala, Option. Some other languages have similar types.


  • :belt_onion:

    Honestly, I personally think the "danger" of playing with null is one of the benefits too.

    The NPE is a great way to ensure you don't try to run things on non-existent things - it's a very loud "No you idiot, this doesn't exist. Don't do that!" message. It prevents people from even attempting things based on something that doesn't exist.

    ED:
    For example, you have a method that frobnicates widgets. If you pass in null, you either have to check for null (in which case you're actively coding a defense against null, which means you're handling it), or you'll throw an NPE somewhere when you actually try to frobnicate the null (surely there's a :giggity: there somewhere). Without a null value, what's the alternative? Silently fail? Or blow up? Which essentially makes the alternative identical to the thing it's replacing...


  • :belt_onion:

    @another_sam said:

    @sloosecannon said:
    @another_sam said:
    Option types

    ?

    In Java, Optional, in Scala, Option. Some other languages have similar types.

    I see.

    So....... what do you do when you call .get() on a value that isn't present? Without null, that's hard to do, yes?



  • @sloosecannon said:

    The NPE is a great way to ensure you don't try to run things on non-existent things - it's a very loud "No you idiot, this doesn't exist. Don't do that!" message. It prevents people from even attempting things based on something that doesn't exist.

    mightBeNull.get() will explode if it has no value if that's what you really want. But you probably want

    mightBeNull match {
        Some(x) => doSomethingWith(x)
        None => wtfDude()
    }
    

    or any one of a number of better constructs.

    @sloosecannon said:

    So....... what do you do when you call .get() on a value that isn't present? Without null, that's hard to do, yes?

    mightBeNull.get() will explode if it has no value. Calling .get() is the explicit acknowledgement I was talking about before, that the programmer has considered the possible null value and decided it will always have a value. There are usually better ways to write it.


  • :belt_onion:

    @another_sam said:

    @sloosecannon said:
    The NPE is a great way to ensure you don't try to run things on non-existent things - it's a very loud "No you idiot, this doesn't exist. Don't do that!" message. It prevents people from even attempting things based on something that doesn't exist.

    mightBeNull.get() will explode if it has no value if that's what you really want. But you probably want

    mightBeNull match {
        Some(x) => doSomethingWith(x)
        None => wtfDude()
    }
    

    or any one of a number of better constructs.

    @sloosecannon said:

    So....... what do you do when you call .get() on a value that isn't present? Without null, that's hard to do, yes?

    mightBeNull.get() will explode if it has no value. Calling .get() is the explicit acknowledgement I was talking about before, that the programmer has considered the possible null value and decided it will always have a value. There are usually better ways to write it.

    Well I think I understand the concept, but I don't really see the benefit.

    I may be missing something but here's how I see it:

    null:
    value is either an object or null.
    null explodes the program if you attempt to operate on it
    null may be compared to prevent explosions


    Option types
    contains a value
    value can't be null, but can be None
    Different methods can be run depending on if value is None or not (similar to a switch statement?)
    Will explode the program if get() is run on something with None as its value.


    So in essence:
    They contain a value or no value
    Can be tested to see if there is a value or not (in syntactically different ways)
    Explode the program if you try to use a non-existent value.

    Is there something I'm missing?

    In essence the differences (as I see them) are:

    • Might explode earlier if the get() call is used before the value would be used
    • Syntactic sugar between match and if(x==null)
      There are differences between the two, granted, but I don't really see a benefit to either option... Option types are essentially a wrapper over null?

  • BINNED

    @ashkante said:

    If you're not required to use Optional, the [Java] compiler (as it is now) won't tell you that you might be missing a null reference check somewhere.

    My bad, I assumed you were using a sane programming language. 😉

    @ashkante said:

    And, returning to my original point - if you abolish null from a language and then add something like Optional<T>, that's just a different name for null/nothing.

    If you're going to do all that work, why not make Optional required?

    @another_sam said:

    mightBeNull.map(notNullValue => dontCallThisIfValueIsNull(notNullValue))

    fmap dontCallThisIfValueIsNull mightBeNull



  • @antiquarian said:

    @ashkante said:
    If you're not required to use Optional, the [Java] compiler (as it is now) won't tell you that you might be missing a null reference check somewhere.

    My bad, I assumed you were using a sane programming language. 😉

    Hehe, fortunately I don't really have to use it. I'm just a "programming-language-groupie" and like to learn about all programming languages (even the bad ones - from those I can learn how not to do things).

    @antiquarian said:

    @ashkante said:
    And, returning to my original point - if you abolish null from a language and then add something like Optional<T>, that's just a different name for null/nothing.

    If you're going to do all that work, why not make Optional required?

    I expect Java people did it for backwards compatibility. I agree with you though, if I put that feature into a new language (or a revamp of an old one), I'd damn well make sure it's compiler-enforced.



  • Optional was introduced quite late in the game (Java 8, released last year), so basically nothing uses it.

    Looking over how it works, it looks like it's intended to be used with Java 8's streams. The methods Java added for stream stuff seems to be heavily influenced by various extension methods in .NET. Kinda/sorta like Java's answer to LINQ, but without the query language itself (streams assume you already have a collection of objects).


  • Discourse touched me in a no-no place

    @powerlord said:

    Kinda/sorta like Java's answer to LINQ, but without the query language itself

    The Java mindset is extremely against putting things like the LINQ syntax in Java. It makes it very stodgy, but also really cuts the amount of shenanigans you can expect to see when reading someone else's code: in Java, your initial reading of the code is virtually always the right one. (And then you wonder “so which class actually implements that stuff?”)



  • @riking said:

    However, it's also not not a number. Don't believe me?

    NaN doesn't incorporate positive or negative infinity, which are infact defined as numbers, but are not finitely quantifiable.

    E.g.

    isFinite(1/0)
    // false
    


  • @sloosecannon said:

    Will explode the program if get() is run on something with None as its value.

    And remember how I said you probably wanted something other than .get()? It's not called all that often, so it stands out as a potential bug when reading the code.

    @sloosecannon said:

    Is there something I'm missing?

    Perhaps only the obviousness aspect of an Option type compared with null. It doesn't do anything that null doesn't do, but it does make it easier to do the right thing, and a bit prettier if you use the syntaxtic sugar. It's not magic, just convenient.



  • @loose said:

    By the way, are you aware that "zero" had to be invented in order to allow Math to be viable?

    The thing that always interested me is that negative numbers and imaginary numbers were accepted at around the same time. In modern education the negative numbers are taught so much earlier, and seem like such a simple extension, that it's difficult to appreciate how much of a conceptual difficulty they presented at the time.


  • Discourse touched me in a no-no place

    @Scarlet_Manuka said:

    The thing that always interested me is that negative numbers and imaginary numbers were accepted at around the same time.

    Well, I think they both originally came out of interest in general solutions for polynomial equations (originally Diophantine equations, but more generally). Negatives would have been accepted sooner; the concepts required for imaginary numbers don't even make sense until you've got negative numbers. 😄



  • My bookkeeping teacher (a technician by education) wondered why bookkeepers wouldn't just use negative numbers instead of putting those numbers on the other side of the account/balance.

    Taking into account that negative numbers were invented only centuries after double-entry bookkeeping, this is obvious.

    Interesting fact: the usual mathematically sound definition of negative numbers as class of pairs of natural numbers is practically identical to the way double-entry bookkeeping denotes more money has been taken from an account than there was on it before.



  • @another_sam said:

    It doesn't do anything that null doesn't do,

    Really? Because I don't remember null having all these methods:

    Method and Description
    
    static <T> Optional<T> empty() 
    Returns an empty Optional instance.
     
    boolean equals(Object obj) 
    Indicates whether some other object is "equal to" this Optional.
     
    Optional<T> filter(Predicate<? super T> predicate) 
    If a value is present, and the value matches the given predicate, return an Optional describing the value, otherwise return an empty Optional.
     
    <U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper) 
    If a value is present, apply the provided Optional-bearing mapping function to it, return that result, otherwise return an empty Optional.
     
    T get() 
    If a value is present in this Optional, returns the value, otherwise throws NoSuchElementException.
     
    int hashCode() 
    Returns the hash code value of the present value, if any, or 0 (zero) if no value is present.
     
    void ifPresent(Consumer<? super T> consumer) 
    If a value is present, invoke the specified consumer with the value, otherwise do nothing.
     
    boolean isPresent() 
    Return true if there is a value present, otherwise false.
     
    <U> Optional<U> map(Function<? super T,? extends U> mapper) 
    If a value is present, apply the provided mapping function to it, and if the result is non-null, return an Optional describing the result.
     
    static <T> Optional<T> of(T value) 
    Returns an Optional with the specified present non-null value.
     
    static <T> Optional<T> ofNullable(T value) 
    Returns an Optional describing the specified value, if non-null, otherwise returns an empty Optional.
     
    T orElse(T other) 
    Return the value if present, otherwise return other.
     
    T orElseGet(Supplier<? extends T> other) 
    Return the value if present, otherwise invoke other and return the result of that invocation.
     
    <X extends Throwable>
    T orElseThrow(Supplier<? extends X> exceptionSupplier) 
    Return the contained value, if present, otherwise throw an exception to be created by the provided supplier.
     
    String toString() 
    Returns a non-empty string representation of this Optional suitable for debugging 
    


  • // 'empty instance'
    SomeClass value = null;
    
    // equality comparison
    Object.equals(null, other);
    
    // predicate filter (convoluted just to illustrate that it can be done with a one-liner)
    var value = new[] { obj }.Where(x=> x != null).Where(predicate).FirstOrDefault();
    
    // mapping
    var value = new[] { obj }.Where(x=> x != null).Select(mapper).FirstOrDefault();
    
    // get value
    object value;
    if ( object == null) { throw NoSuchElementException; }
    value = object;
    
    // get hashcode
    int hashcode = obj != null ? obj.GetHashCode() : 0;
    
    // if present
    if ( obj != null ) { consumer( obj ); }
    
    // is present
    bool value = ( obj != null );
    
    // or else
    object value = obj ?? new object();
    
    // or else get
    object value = obj ?? other();
    
    

    Trivial and all expressed using null in the internals. In other words Optional<T> does indeed not do anything that null doesn't do.



  • @Ragnax said:

    Trivial and all expressed using null in the internals. In other words Optional<T> does indeed not do anything that null doesn't do.

    Curious that you use C# code as an example, as C# has Nullable<T>, the same type of concept behind Optional.
    You can't assign a DateTime? to a DateTime without explicitly casting to the value, and doing so when it has no value throws an exception. DateTime? can be compared with null, however it is never actually null itself.

    The issue is not of Null vs Optional, it's of compiler support and documentation. Whether or not something may exist versus definitely existing.
    C# screwed it up by not using the ? syntax for reference types as well.
    Java screwed it up by having no compiler support.



  • Actually you should say that C# screw it up by not having NonNullable<T> for reference type (adding nullability for reference type is just redundant and not adding actual value to the language itself) And reversing the meaning of ? for reference type makes sure you breaks almost all existing code.

    So it's too late by the time nullable related operators are added into .NETv2. Btw, these are all just syntax sugars and I can live without these.



    1. just like for doesn't do anything you couldn't do with a goto
    2. you got a lot of statements there, though. Being free to call methods on your nullable type gives you a lot more freedom to choose the appropriate coding style for what you're trying to accomplish
    3. I personally don't like the c# designers' tendency toward syntax bloat. A class full of helper methods is the proper object oriented way to handle this, and given all of these operations can be done just as tersely—and twice as clearly—using optional, their decision to add a special new syntax just for boxing structs seems kind of pointless. Maybe they need to start at -200 from now on
    4. bottom line is: null doesn't actually do anything. It just is (or is not, depending on your philosophy).

Log in to reply