|
ChristianNeumanns wrote: The infamous problem of null pointer errors (caused by 'null') is elegantly solved in languages with compile-time null-safety - they eliminate the risk for null pointer errors.
I'm not sure anything "eliminates" the risk, though it certainly reduces it. Compile-time null checking is a hack IMO, to deal with a problem caused by the introduction of null reference pointers. Simply put, we should not be able to assign any value to a pointer that is not a valid memory address. To go even further, one should not be able to assign any value to a pointer that is not a validly pointed to object. More on this later.
ChristianNeumanns wrote: What value would you then store in variable 'productDeliveryDate' if the product has not yet been delivered?
As I mentioned in my last message, you have a "Nothingable" type (similar to the C# Nullable<t> type but useful for reference types as well). I don't really like the name, but don't have a better one at the moment. In your example, you have Date? which is not actually a null value, but indicates that it does not hold a valid value either. In this case, you can say productDeliveryDate.HasValue == true/false, or you can use coalescing operators like ?? or the new C# ?. operator to basically do nothing on an object with no value.
This is something we can more or less do today without language support (I am guessing we can override the ?? and ?. operators, if not it may require some support).
ChristianNeumanns wrote: The risk for bad outcomes, due to the fact that we simply iterate over the list and do nothing, instead of checking for emptiness and differentiating the cases
That is more of a methodology or process issue than anything else. If there is some logical difference between empty and non-existent then it's easy to differentiate them.
For instance, there is a difference between a Store with no inventory and no store at all. But you should not even be executing code to iterate over inventory if there is no store at all. You should not even be there. Null is a database concept because it represents the empty set of a join, or it represents the absence of data in a column. Null in program code represents "Something which shouldn't ever exist" and therefore is often used to indicate lack of existence.. End of pointers arrays, end of linked lists, end of files, uninitialized states, etc... all of those things can also be represented by explicit objects in an object oriented language, and with much greater semantic meaning.
If you're talking about a non-object oriented language like C or assembly, then sure... Null is probably your only choice for an indicator of something that can't otherwise exist as normal data, unless you basically design an OOP type system into your application. But in in almost any modern OOP language, null has outlived its usefulness in my opinion.
--
Where are we going? And why am I in this handbasket?
|
|
|
|
|
|
I agree that no result available vs empty collection are two different things, awareness of that is good. That being said, advocating null references - an already commonly abused concept - is very bad advice.
The problems stated in these examples don't justify null references and can be solved in much better and self-documenting ways. Eg Bob's postage stamps:
interface Person {
Optional<StampCollector> stampCollection();
}
interface StampCollector {
List<PostageStamp> postageStamps();
// other properties of a collector
}
What's the difference? Reading Person makes it obvious that not all people collect stamps, and need different handling. The type StampCollector is free to define it's invariants/constraints (you'll need more than the special case of having at least one element in some collection). No fiddling with special cases, null references, obscure/implicit tricks.
The same pattern can be applied to all the other examples similarly. Note that Optional/Maybe does not need language support as stated in the Note section. If it doesn't come out of the box, there are libraries for it.
Edit: html escape
modified 25-Jan-15 18:19pm.
|
|
|
|
|
mrcellux wrote: The problems stated in these examples don't justify null references and can be solved in much better and self-documenting ways
Really?
mrcellux wrote: <layer>Reading Person makes it obvious that not all people collect stamps, and need different handling.
Yes, this is a clear advantage of the Optional pattern - your intention is clear.
However we have the same advantage with 'null' in a null-safe language (which I advocate in my articles). The 'nullable' keyword documents our intention:
public nullable List<Stamp> postageStamps();
What's more, the 'nullable' keyword informs the compiler that returning 'null' is allowed and ensures we can't forget to make a distinction between the two semantically different cases 'there are stamps' and 'there are no stamps' (i.e. client code must check for 'null').
On the other hand, misuses of the Optional pattern (as implemented in Java) cannot be prevented by the compiler. For more information please refer to chapter 'The Optional/Maybe Pattern' in my previous article Why We Should Love null[^]
For example, nothing can prevent a class that implements interface 'Person' to accidentally return 'null';
Another advantage of the 'nullable' keyword is that this important information can be retrieved and used by tools (IDE, static code analyzers, etc.) in a reliable way.
mrcellux wrote: No fiddling with special cases, null references, obscure/implicit tricks.
With the solution you suggest we have to check 4 things if we really want to be on the safe side:
1. check if stampCollection() returns null
2. check if the Optional returned by stampCollection() contains a value
3. check if postageStamps() in StampCollector returns null
4. check if postageStamps() returns an empty list
And there is no compiler support for reminding us to do these checks if we omit them.
Now compare this with the null-safe version: There is no ambiguity in the API. There is only a single check to be done by client code (e.g. if stampList is null then ...). And the compiler ensures we don't forget this check.
mrcellux wrote: Note that Optional/Maybe does not need language support as stated in the Note section.
Where did you see this ('stated in the Note section')?
As far as I know I never ever said that the Optional/Maybe needs language support.
mrcellux wrote: If it doesn't come out of the box, there are libraries for it.
Yes, and if there is no library support then we can very easily create an Optional type ourselves (if we really need it).
|
|
|
|
|
To your list of 1-4 i could add a fifth: even the Optional reference could be null. And this is my point. Systems relying on null as an encouraged option turn into a NullPointerException minefield, and developers often end up in speculative null checking frenzy.
Consider avoiding null as a convention - if forced to for some reason, document nullability as explicitly as possible. In such a context the code reads differently. We see that a Person may or may not be a StampCollector. We can also see what information we have on a stamp collector. We don't have to guess or look up what references may be null and check them - we can assume none of them are. The code should express this information, using null references in eg Java hides this.
Making StampCollector an explicit domain concept has some more benefits too. If being a collector means the person must have at least one stamp, it can be made a StampCollector class invariant. Note that constraining some list to have at least one element is a very special case, there is a lot more flexibility you'll need in real systems to express the domain. You can't often do that with special references.
|
|
|
|
|
The entire premise of this article is misleading, and absurd.
First of all, NULL != No Results in programming as many have pointed out. It means a pointer without a destination. Now the semantics of that value has slightly changed over time, but let's not confuse the NULL DB value with a NULL programming value. In addition, there is absolutely no correlation between a real world list, and a NULL pointer. You cannot make a NULL list in real life. If you wanted to get close, asking your wife for a list, and making somewhere to store it, but not having her response yet would perhaps come close.
Second, since NULL means an uninitialized pointer, it holds a critically important role that you are steamrolling by using it for something it was not designed for. I love using NULL when appropriate, so don't think I am discouraging it, I just am pointing out it's correct usage.
With your suggested pattern I am shackled without the expressive ability I have without it.
Consider this scenario:
I have a list of Allergies that takes some time to compute.
Because of this I only want to compute them when I need them.
Sometimes I may not have any allergies
Once I know the allergies I shouldn't calculate them again.
With your pattern, I cannot complete this without significant kludge and additional tracking overhead.
Using NULL correctly, I can know the list is uninitialized, calculate the allergies, and know that no matter how many results are there, if I have a list, I do not need to reload. Basic Lazy loading.
List<allergy> allergies = NULL;
...
If(allergies == NULL)
allergies = GetAllergies();
DoStuffUsingAllergies(allergies);
|
|
|
|
|
Member 10433332 wrote: NULL != No Results in programming
I never said the opposite (or at least I didn't intend to say the opposite). We can think of null as 'the absence of data', and this is a valuable piece of information. A function that returns 'null' tells us that there are ... well, no data, which is a result (e.g. a function that returns null for a delivery date tells us that the delivery date is unknown, and this is valuable information).
Member 10433332 wrote: let's not confuse the NULL DB value with a NULL programming value
Could you tell us why you think that the concept of null in a DB is different from null in code?
Member 10433332 wrote: there is absolutely no correlation between a real world list, and a NULL pointer
The correlation is this: Null in code typically means that there is no list in real life. For example, if variable to_do_list points to null it means that there is no to do list (or the list is unknown).
Member 10433332 wrote: NULL means an uninitialized pointer
Sorry, but I fully disagree. A variable that points to null is initialized (because it points to null). And the compiler should track this. An uninitialized variable cannot be used in an expression, but a variable that points to null can very well be used. That's why the following Java code compiles:
String s = null;
System.out.println ( s );
But this code generates a compilation error:
String s;
System.out.println ( s );
Member 10433332 wrote: NULL ... holds a critically important role that you are steamrolling
Maybe I misunderstand, but I wonder why you think I am steamrolling null. Exactly the opposite is true. That's why I chose Why we should love 'null'[^] as title for my first article.
Member 10433332 wrote: With your pattern, I cannot complete this without significant kludge and additional tracking overhead.
Sorry, but I don't get your point.
And I can't understand the null check in your example:
List allergies = NULL;
...
If(allergies == NULL)
allergies = GetAllergies();
|
|
|
|
|
ChristianNeumanns wrote: I never said the opposite (or at least I didn't intend to say the opposite). We can think of null as 'the absence of data', and this is a valuable piece of information. A function that returns 'null' tells us that there are ... well, no data, which is a result (e.g. a function that returns null for a delivery date tells us that the delivery date is unknown, and this is valuable information).
Again, NULL is not the absence of data. It is like I said earlier, a pointer with no destination. This can easily be used to know that we do not yet know an answer, but it does not definitively tell us that the result was that there is nothing, especially for lists.
ChristianNeumanns wrote: Could you tell us why you think that the concept of null in a DB is different from null in code?
NULL in programming is a pointer that has no destination. NULL in a DB is a special value specifically reserved to indicate "I don't know!". A null BirthDate in the DB for a customer, for example, indicates we don't know the birthdate. Obviously, there is a birthdate (otherwise wouldn't be a customer) but *we don't know it*. You can use the same concept programmatically if you so choose, (To treat a NULL pointer/value as unknown), and I often do. However it is important to understand at the root of it all, NULL in code simply means uninitialized.
ChristianNeumanns wrote: The correlation is this: Null in code typically means that there is no list in real life. For example, if variable to_do_list points to null it means that there is no to do list (or the list is unknown).
AHA! Your starting to see the issue! Which is it, no to-do list, an unkown to-do list, or a now empty to-do list. You have lost that expressiveness by misusing NULL.
ChristianNeumanns wrote: Sorry, but I fully disagree. A variable that points to null is initialized (because it points to null). And the compiler should track this. An uninitialized variable cannot be used in an expression, but a variable that points to null can very well be used. That's why the following Java code compiles:
First of all, there are a ridiculous number of programming languages and some that do many things odd, so I wouldn't use one languages behavior as a bedrock of how things should be done anywhere but that specific language. Having said that, I hope you realize that is a code convenience feature by the JAVA compiler for local variables (since it can be ambiguous for the next person maintaining the code) that it requires local variables to be defined. Make an array of object values, and I think you will see without initializing the object pointers, they are all NULL. Moreover, I have worked in a large number of languages, most of which work that way ( most of which allow you to be ambiguous about local variables as well).
ChristianNeumanns wrote: Sorry, but I don't get your point.
And I can't understand the null check in your example:
Sorry, not JAVA code there. Psuedo C#. The point was that later in code when you want to know the allergies you can check for NULL, and load the allergies if they have not been loaded. If we return an empty list when the person has no allergies, we can now tell the difference between "I have no allergies" and "I don't know if I have any allergies" without any additional logic, or hackish additional variables.
ChristianNeumanns wrote: Maybe I misunderstand, but I wonder why you think I am steamrolling null. Exactly the opposite is true. That's why I chose Why we should love 'null'[^] as title for my first article.
Because you are making NULL lose syntactical meaning by overusing it.
|
|
|
|
|
I think you are way to dogmatic about this.
4 Articles about one single issue... I don't get it.
Anyway:
I'm programming in the .Net world and because of that, I try to follow the guidelines and best practices of the framework.
At least it should be easier for new colleagues to read an understand my code if I follow the standards.
So my question is: What is the standard in the .Net world?
Look here:
http://msdn.microsoft.com/de-de/library/dn169389(v=vs.110).aspx
"X DO NOT return null values from collection properties or from methods returning collections. Return an empty collection or an empty array instead."
Other platforms or frameworks may require other standards and special situations may require a different solution.
|
|
|
|
|
Malte Klena wrote: I try to follow the guidelines and best practices of the framework.
That's good and that's exactly what I suggested already in the final conclusion of part 2[^]:
"For example, if we work in an environment where 'return an empty collection and not null' is the rule and applied by all members of the team, then we should do the same, even if we are convinced that returning null is better. Swimming against the tide creates inconsistencies and leads to other evil problems."
Malte Klena wrote: What is the standard in the .Net world? Look here: http...
That's exactly the link I also mentioned already in part 1[^] (see section "What's the Problem?")
So, while this is the standard which every developer working in this environment should respect and apply, I (and many other people) firmly believe that there is a better standard. The reasons are explained in part 2[^].
|
|
|
|
|
I still disagree with the premise of using null in this manner, but I also feel the Shopping List example is a very poor one. If software does the digital equivalent of driving to the store with an empty shopping list, then the code is inefficient and incomplete. Just as if Bob drives to the store with an empty list, he's lazy and/or stupid.
I still don't see the point of using null for anything other than an uninitialised variable in any but the most speed/memory-sensitive applications. *shrug*
|
|
|
|
|
DevKnightlie wrote: If software does the digital equivalent of driving to the store with an empty shopping list ...
I saw software that sends empty lists over a distant network connection in order to 'do nothing'.
There are many other real-life examples of software doing crazy things.
DevKnightlie wrote: ... then the code is inefficient and incomplete
True.
DevKnightlie wrote: Just as if Bob drives to the store with an empty list, he's lazy and/or stupid
Yes.
But aren't we all sometimes lazy and/or stupid? If we weren't 'stupid' we would be able to write bug-free code - but reality shows we are still very far away from 'number_of_bugs = 0'. As for myself I am often ashamed when I re-read my lousy code written in the past (e.g. 6 months ago), although I love coding and do it for over two decades.
What we really need to protect us from ourselves, therefore, are fail-fast features natively built into the languages and standard libraries. Compile-time null-safety (which I advocate in my articles) is just one example of such features.
DevKnightlie wrote: I still don't see the point of using null for anything other than an uninitialised variable ...
A variable that points to 'null' is not uninitialized. It has a value, namely 'null'. The meaning of 'null' depends on the context. It is a valuable information.
The usefulness of 'null' is explained in part 2[^] and part 4[^], as well as in Why we should love 'null'[^].
Cheers
|
|
|
|
|
Well written article, but I am afraid I have to agree with the other critiques here. I just checked my current project and I have 800 different Lists in source code, having to add null checks to each user of any of these is ridiculously complex and unnecessary. In that whole code base (about 77k lines of code) there are only 264 null checks (and 80% of those are in tests or framework specific code) and I am pretty proud of that fact.
Also how could your advice be used in a language that does not have null (like functional programming languages)?
|
|
|
|
|
DeltaEngine wrote: Well written article
Thank you.
DeltaEngine wrote: I have 800 different Lists in source code, having to add null checks to each user of any of these is ridiculously complex and unnecessary
In null-safe languages (which I advocate in my articles) there are two cases:
1. lists that can be null, e.g.
nullable List<Order> getCustomerOrders ( String CustomerID )
2. lists that cannot be null, e.g.
List<ComputerUser> getComputerUsers()
Lists that cannot be null don't need to be checked for null, so you wouldn't have to add null checks for all of your 800 different lists. The compiler (of a null-safe language) even emits an error if we accidentally check for null, because it doesn't make sense.
On the other hand, lists that can be null have to be checked in the source code (verified by the compiler), and there is a good reason for this: If a list can be null then it means there are two semantically very different cases that have to be treated differently (at least in most situations). Some examples:
- if there are no children in the classroom then we have to switch off the lights, close the windows, etc.
- if the patient has allergies then he/she must be treated differently
- etc. etc.
As explained in part 2[^] it is often crucial to differentiate both cases - forgetting to do so can lead to harmful outcomes (see examples in part 2). There are rare situations in which it is not necessary to differentiate. But being nevertheless forced to check for null (in null-safe languages) leads to code that clearly documents our intention to treat both cases the same way.
To sum up:
In a null-safe language the compiler enforces the following:
- we can't forget to check for null if it is necessary to do so (i.e. nullable type)
- we can't check for null if it wouldn't make sense (i.e. non-nullable type)
This is the ideal solution and leads to more reliable code.
DeltaEngine wrote: how could your advice be used in a language that does not have null
Every language needs some way to express 'the absence of a value' (e.g. the product's delivery date is unknown).
Many languages use null, but some languages (mostly functional programming languages) use the Maybe/Optional pattern.
Please refer to my previous article Why we should love 'null'[^] for a discussion of alternatives to 'null' (and have a look at chapter 'The Optional/Maybe Pattern').
Cheers
|
|
|
|
|
Hey, would be nice to insert the links to the other parts in this one as well
|
|
|
|
|
Message Closed
modified 2-Feb-15 22:59pm.
|
|
|
|
|
Coding is not meant to simulate "real life" in a lot of cases, and an empty list seems like more appropriate in places such as getting a list of customers in a place. We are not giving that to a boss. I don't disagree that in some cases, returning null may have some meaning, but always returning null instead of an empty list does usually not make sense.
|
|
|
|
|
Elrond wrote: Coding is not meant to simulate "real life" in a lot of cases
I think that quite the opposite is true in many cases, especially in high level languages.
A lot of applications model the real-world. Some examples:
- search the net for terms like 'software simulation examples'
- a typical business application, like an ERP, models objects (data and behavior) of the real world, such as customers, suppliers, products, invoices, etc.
- neural networks software applications are inspired by real-world biological neural networks (see Wikipedia)
- the laws of nature applied in all kinds of scientific software applications, games, etc.
As I said already in another comment:
Asking myself
"How would this be done in real-life"
often helped me whenever I struggled to find a good design for data and behavior.
|
|
|
|
|
See more elaborate post below.
|
|
|
|
|
It's up to the API designer to think about what return values he wants, this is not really limited to lists. In generally I would aim for simplicity, and therefore not return null results if possible, so it saves the caller some exceptions. Usually things won't crash when dealing with empty lists, but just run fine as there's nothing in the list to process.
Sometimes you want non-value types to behave like value types. E.g. string, use string.Empty as an empty value and save yourself NullReferenceException headaches. Lists are the same. You can use e.g. use IList<> as a return value, and have a static array as an empty value (immutable), so you are not forced to create an empty list every time (so equal performance to returning null, analogous to string.Empty). Alternatively you could even make your own (generic) empty list implementation, again using a singleton pattern.
Sometimes you want value types to also have a null option, hence the nullable types (although still value types).
Altogether, compilers should become a bit more powerful so the API designer can specify whether returned non-value type can return null or not. Maybe this is already possible in recent compiler versions? I haven't really followed the recent changes in C#.
Wout
|
|
|
|
|
|
wout de zeeuw wrote: generally I would aim for simplicity, and therefore not return null results if possible
Yes, non-null should be the default, and the API should unambiguously indicate input/ouput that might be null.
wout de zeeuw wrote: Usually things won't crash when dealing with empty lists, but just run fine
As explained in the article, returning empty lists (and forgetting to check for emptiness) increases the risk for outcomes that are worse than a crash due to a null pointer error, because the program might silently continue execution with wrong data. Example: method getAllergiesOfPatient returns an empty list, because an allergy test has not yet been done, but the doctor wrongly assumes that the patient doesn't have allergies (because the result is displayed in ambiguous way).
wout de zeeuw wrote: compilers should become a bit more powerful so the API designer can specify whether returned non-value type can return null or not
True!
Differentiating between nullable and non-nullable types is crucial.
wout de zeeuw wrote: Maybe this is already possible in recent compiler versions?
Yes, there are some languages supporting this.
For an (uncomplete) overview you might look at chapter 'So, Is There A Better Solution?' in the article Why We Should Love 'null'[^]
|
|
|
|
|
I got the feeling that the article assumes that empty lists are somehow invalid or at least special. That's absolutely untrue - most list operations (size, iteration, map, etc.) are valid on an empty list and work as expected. Yes, first() and last() will throw but then again so will taking 7th element of a six-element list - does that make lists of length six invalid? In all my experience with programming I have very rarely needed to handle empty lists specially. On the other hand, null is invalid for all list operations, therefore it must always be treated separately.
You claim that it is better to return null than empty list, because than you just have to check for null and not for both null and empty list. Well, if the function never returns null, then you just have to check for empty list, and even that is usually not necessary. How often do you need the first element of a list? What you usually do is loop over the list and that works for empty list just as well as for any other.
I believe that the examples from parts 1-3 of the article hardly justify the point you are making - just the opposite.
1) The listFiles() method. This is a typical example of a poorly written method - a list method returning null. I'm certain many of its users wanted to demolish the authors car after their program died in production because of a null pointer exception . (I had one such nasty surprise with javax.mail.internet.MimeMessage.getRecipients which returns null when a recipient list is empty; if people take your advice seriously, such ugly functions will become common.)
2) Listing elements of collection. This is really a presentation issue. When you present a list to users, you usually show some header before elements so an empty list is just a header without any data, which is quite understandable to anybody. Moreover, most view templating engines have syntax for special presentation of empty lists (e.g. for ... else ...) so if you want, it is easy to show some more information in the empty case. On the other hand, if you have null instead of empty list, the users will quite likely receive "500 Internal server error" (or some other erroneous result, like an empty screen). I would say it's quite likely that is what happened in your biking results example.
3) House prices and voting results. These examples are very similar and have similar flaws: assume that you have your "null for no data" representation for voting results. Before any data is entered for a particular politician, you have null result. But what happens when somebody enters only part of data, e.g. first 10 out of 10000 votes? Your results are wrong again. So what you really want to distinguish is not 'no data' vs. 'some data' but 'partial data' vs. 'complete data' and you can only do this using some additional flag. Likewise in the housing example, if someone entered just the price of windows, you might get the house really cheap .
I must admit these two examples would work if you assume that the data is always complete, but then you still need both null and empty list (what if actually nobody voted for some politician?) That may be a problem if you store the list in a SQL database where there are no null collections, so you need an extra flag anyway.
4) Real life analogies. It is true that in real life we will have no list instead of an empty list. For humans such special cases and micro optimizations are actually easier to handle then some uniform algorithm. But then again, for humans recognizing a face in a picture is much easier the adding a thousand numbers together; for a computer, exactly opposite is true. In programming, special cases are always a maintenance burden and are to be avoided unless they are required by the target domain or give significant performance benefits. Replacing empty list with null is neither. (Unless you're running on a microcontroler, but then you're using C instead of Java, arrays instead of lists and pool allocation instead of new... after you do all that it might pay off to replace empty arrays with nulls.)
|
|
|
|
|
icokk8 wrote: In all my experience with programming I have very rarely needed to handle empty lists specially.
That is amazing, because there are so many situations in which the cases "there are data" and "there are no data" are semantically very different and therefore need to be treated differently. It is easy to come up with uncountable examples:
- if "we have customers in Singapore" then we have to plan a trip to Singapore, otherwise we haven't.
- if the list of available rooms in a hotel is empty then we have to choose another hotel
- if the classroom is empty then: switch off the lights, close the windows, etc.
- if my to-do list is empty then I can go for a walk
- etc. etc.
icokk8 wrote: You claim that it is better to return null than empty list, because than you just have to check for null and not for both null and empty list.
Yes, I profoundly believe it is better to return null, but not for the reason you mention.
The main reason is this:
If we forget to make a difference between the two semantically different cases of "there are data" (i.e. a non-empty list) and "there are no data" (i.e. an empty list or null) then the average risk for bad outcomes is less high if we return null. The reason for this is explained in the article series (especially part 2[^] and part4[^]) and illustrated with concrete source code examples. In short:
Forgetting to check for null typically leads to a null pointer error which can't stay unnoticed and:
- leads to an application crash (which is of course undesirable, but (in most cases) less disastrous than continuing execution with wrong data and/or wrong behavior)
- is detected early in the development process
- is easy and cheap to locate and repair
On the other hand, forgetting to check for an empty list often leads to the program silently continuing execution, possibly with wrong data and/or wrong assumptions, ending up in really bad situations (example: the doctor assumes the patient has no allergies, because the list of allergies reported by the software is empty, because an allergy test has not yet been done). This kind of bugs are less likely to be discovered before software delivery and sometimes they are difficult to locate (because of the far distance in time and source code location of the cause and effect).
Another advantage of returning null is that null is always cheap in time and space (see chapter 'Time and space requirements' in part 2[^]).
Finally, there is another unbeatable advantage that exists in languages with compile-time null-safety: Forgetting to check for "no data" is no more an issue, because the compiler gently reminds us to do so. For a concrete example, you might want to have a look at chapter 'Example 4' in part 4[^].
icokk8 wrote: What you usually do is loop over the list and that works for empty list just as well as for any other.
Yes, that is why people like to work with empty lists: Just loop over the list and don't worry about the list being empty (and the potential for bad consequences), and don't worry about null pointer errors.
icokk8 wrote: I'm certain many of its users wanted to demolish the authors car after their program died in production because of a null pointer exception
Might be, but the authors car would have been saved in a language with compile-time null-safety.
icokk8 wrote: When you present a list to users, you usually show some header before elements so an empty list is just a header without any data, which is quite understandable to anybody.
It might be understandable for some users. But others might wonder: "Is the list really empty or was there a problem printing the list?".
On the other hand, making a difference by explicitly printing a message like "There are no data" eliminates the ambiguity.
icokk8 wrote: most view templating engines have syntax for special presentation of empty lists
Yes, and this is good! So, these templates differentiate between "data" and "no data"
icokk8 wrote: On the other hand, if you have null instead of empty list, the users will quite likely receive "500 Internal server error"
Yes, and the user will not be happy. But:
This bug is more likely to be detected before delivery, because the programmer/tester quickly gets aware of it the first time it happens before delivery. Also, the bug is more likely to be fixed quickly after delivery if there is a good error logging/reporting system in place. Moreover displaying "500 Internal server error" is not nice, but less painful than displaying wrong data or continuing with wrong behavior due to wrong assumptions.
icokk8 wrote: But what happens when somebody enters only part of data, e.g. first 10 out of 10000 votes? Your results are wrong again.
Yes, then we get wrong results, whether we return null or an empty list.
Using null and compile-time null-safety helps to detect some bugs early. But in this case it's not a software bug - it's a data entry error. And some kinds of user entry errors (e.g. entering a list only partially) can't be detected by software and software cannot be blamed for such problems.
icokk8 wrote: In programming, special cases are always a maintenance burden
I fully agree.
And that's why I advocate using null, ideally in a null-safe language. Because some corner cases will be discovered and reported by the compiler if we forget to handle them separately. For an example see chapter 'Example 4' in part 4[^].
Cheers
|
|
|
|
|
(both doens't change your conclusion of 'failing early', I just you use the wrong arguments)
Quote: It might be understandable for some users. But others might wonder: "Is the list really empty or was there a problem printing the list?".
this argument doesn't count, as there should be an exception thrown on a problem.
Quote: But in this case it's not a software bug - it's a data entry error. And some kinds of user entry errors (e.g. entering a list only partially) can't be detected by software and software cannot be blamed for such problems.
Here's a circle in your reasoning:
It *is* indeed a problem with the software and the software *will* be blamed. It will be blamed in the same way as a NullException or wrong data as of the empty lists.
As I get you right, you want to return Null, so that these kinds of bugs become obvious. You could also say "an empty sheet of paper is not a software bug. There are just no patients in that hostpital". So not yet registered patients - and therefore an empty list - might also be a data entry error, that we don't need to handle
|
|
|
|
|