Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??

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:

  • The value's not applicable
  • It's not known
  • It's not available
  • It's restricted
  • Something else?

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.


In reply to "undef" is not NULL and what to do about it by Ovid

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (3)
As of 2024-04-19 17:51 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found