http://www.perlmonks.org?node_id=1019806

What's wrong with undef?

Currently undef has three different coercions: false, zero, or the empty string. Sometimes those are correct, but not always. Further, by design, it doesn't always emit warnings:

$ perl -Mstrict -Mwarnings -E 'my $foo; say ++$foo' 1 $ perl -Mstrict -Mwarnings -E 'my $foo; say $foo + 1' Use of uninitialized value $foo in addition (+) at -e line 1. 1

And because it has no precise definition, undef might mean any of a number of things:

In other words, the behavior of undef is overloaded, its meaning is ambiguous and you are not guaranteed to have warnings if you use it incorrectly.

Now think about SQL's NULL value. It's problematic, but no alternative has taken hold for simple reason: its meaning is clear and its behavior is unambiguous. It states quite clearly that 'if I don't have a value, I will treat that value as "unknown" via a set of well-defined rules'.

So, between the ambiguity of Perl's "undef" and the clarity of SQL's NULL, it's probably not surprising that some code could use the latter instead of the former. In fact, I will go further as to assert that the code is often clearer if you do that. Case in point:

foreach my $employee (@employees) { if ( $employee->salary < $threshold ) { increase_salary( $employee, 3_000 ); } }

I'm sure plenty of you see the bug: what if the employee doesn't have a salary? I've already outlined several reasons above for why the salary might be undefined, not all of which mean "this employee has no salary". So if salary is undefined, you will probably get a warning and you'll have a bug. If threshold is undefined you'll get a warning but you won't have a bug. Fun, eh? So you might rewrite the code like this:

if ( defined $threshold ) { foreach my $employee (@employees) { my $salary = $employee->salary; next unless defined $salary; if ( $salary < $threshold ) { increase_salary( $employee, 3_000 ); } } }

Now that's starting to get ugly. Wouldn't it be if we had simple, clear NULL semantics instead? Now you can, with Unknown::Variables. I explain this idea at length at blogs.perl.org, but it works like this: instead of using "undef", you use "unknown".

use Unknown::Variables; my $value = unknown; my @array = ( 1, 2, 3, $value, 4, 5 ); my @less    = grep { $_ < 4 } @array;   # assigns (1,2,3) my @greater = grep { $_ > 3 } @array;   # assigns (4,5)

There's a lot more to this module than meets the eye, but the idea is that the unknown keyword (well, it acts like a keyword), unlike undef, has clear, well-defined semantics and and predictable behavior.

In the employee salary example above, if an unknown salary returns unknown instead of undef, our code goes back to the original:

foreach my $employee (@employees) { if ( $employee->salary < $threshold ) { increase_salary( $employee, 3_000 ); } }

Further, you won't generate any warnings because warnings are there to warn you when you're doing something bad. In this case, unknown values are designed to be compared an behave appropriately. And as you can see, our code above becomes much simpler. When used appropriately, unknown values can let your code focus on the problem rather than work around fiddly bits of the language. Any place that you find yourself assigning undef in your code, see what would happen if you put an unknown value there instead.

Feedback welcome. Download the code from the CPAN or, if you want to hack on the code, clone it on github.