Skip to content
VLSI Mentor

Perl Part I · Module 6 · Hashes

exists vs defined vs Truth

There are three questions you can ask about a hash key and they are not the same question. Was this key ever set at all? If it was, does it hold a real value or nothing? And if it holds a value, is that value true? Most code asks the third by accident, because putting a hash element straight into an if is the shortest thing to write — and the third question is the one you almost never mean. In this trade the numbers that matter are error counts, violation counts and failure counts, where the good answer is zero, and zero is false. So the obvious test reports a block with no errors in exactly the same way it reports a block that was never tested, which is the most expensive confusion a report can contain.

Foundation16 min readPerlPart IFundamentalsHashesDebuggingContext

Part I · Module 6 · Chapter 6.4 · exists vs defined vs Truth

1. Three Questions

TestAsksAnswers no when
exists $h{k}was this key ever set?the key is not in the hash
defined $h{k}does it hold a real value?the key is absent or its value is undef
if ($h{k})is that value true?absent, undef, 0, "0" or ""

They are nested: anything that fails exists also fails defined, and anything that fails defined also fails the truth test. But the reverse is not true, and the gaps between them are where the bugs live.

Chapter 2.7 established definedness and Chapter 2.8 established truth. This chapter is those two plus a third question that only containers have — because only a container can be asked whether a member is there at all.

2. The Full Matrix

Azvya Education Pvt. Ltd.VLSI Mentor
matrix.pl
use strict;
use warnings;

my %h = (
    pass      => "PASS",
    zero      => 0,
    zero_str  => "0",
    double_0  => "00",
    empty     => "",
    emptied   => undef,
);
# 'absent' is deliberately never set

printf "%-10s %-8s %-8s %s\n", "key", "exists", "defined", "true";
printf "%-10s %-8s %-8s %s\n", "-" x 10, "-" x 8, "-" x 8, "-" x 4;

for my $k (qw(pass zero zero_str double_0 empty emptied absent)) {
    printf "%-10s %-8s %-8s %s\n", $k,
        (exists  $h{$k} ? "yes" : "no"),
        (defined $h{$k} ? "yes" : "no"),
        ($h{$k}         ? "yes" : "no");
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
key        exists   defined  true
---------- -------- -------- ----
pass       yes      yes      yes
zero       yes      yes      no
zero_str   yes      yes      no
double_0   yes      yes      yes
empty      yes      yes      no
emptied    yes      no       no
absent     no       no       no

Seven states, three tests, and the three tests disagree on four of the seven rows.

Read the rows that matter:

  • zero — the value is 0. It exists, it is defined, and it is not true. This is the row that causes real damage.
  • double_0 — the value is "00". It is true, while "0" is not. "00" is not one of Chapter 2.8's false values, so it is true like any other non-empty string. Nobody predicts this one.
  • empty — the empty string exists and is defined and is false.
  • emptied — set to undef, so it exists but is not defined. That is Chapter 6.3 §4's state.
  • absent — never set. The only row where exists says no.

3. The Three Questions, Drawn

One key holding zero, asked three different questions

A fan with seven blocks. At the middle left is a single hash entry whose value is the number zero, representing a block that was tested and found to have no errors. Three branches lead to the right, one for each question. The top branch asks whether the key exists and answers yes, because the key is present in the hash even though its value is zero. The middle branch asks whether the value is defined and also answers yes, because zero is a real value rather than an absence. The bottom branch puts the value straight into an if condition, asking whether it is true, and answers no, because zero is false in Perl — which is the silent trap, since this branch cannot be distinguished from a key that was never set at all.One key$errors{alu} is 0exists $h{k}was it ever set?YESthe key is presentdefined $h{k}a real value?YES0 is a real valueif ($h{k})is the value true?NO0 is false — a trap12
The key is present and its value is a legitimate measurement of zero. Two of the three tests say so; the third says no, because zero is false. Nothing in the hash has changed between the three branches — only the question being asked.

4. The Hazard, Measured

Azvya Education Pvt. Ltd.VLSI Mentor
zero-errors.pl
use strict;
use warnings;

my %errors = (alu => 0, fifo => 7);

print "using if (\$errors{\$block}):\n";

for my $block (qw(alu fifo decoder)) {
    if ($errors{$block}) {
        printf "  %-8s has errors: %d\n", $block, $errors{$block};
    }
    else {
        printf "  %-8s reported as NO DATA\n", $block;
    }
}

print "\nusing exists:\n";

for my $block (qw(alu fifo decoder)) {
    if (exists $errors{$block}) {
        printf "  %-8s measured %d error(s)\n", $block, $errors{$block};
    }
    else {
        printf "  %-8s was never measured\n", $block;
    }
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
using if ($errors{$block}):
  alu      reported as NO DATA
  fifo     has errors: 7
  decoder  reported as NO DATA

using exists:
  alu      measured 0 error(s)
  fifo     measured 7 error(s)
  decoder  was never measured

Read the first block of output. alu and decoder got the same report, and they are completely different situations:

  • alu was tested and had zero errors — the best possible outcome.
  • decoder was never tested — an unknown.

The truth test cannot tell them apart, because 0 is false and an absent key is false, and if only sees false.

The second block gets both right, because exists asks the question that actually distinguishes them.

5. Which Test to Use

The question to ask yourself is what would it mean for this to be missing?

You want to knowUse
did this test run at allexists $h{$k}
did it produce a valuedefined $h{$k}
is the value non-zero / non-emptyif ($h{$k}) — deliberately
the value, or a default if absent$h{$k} // $default
Azvya Education Pvt. Ltd.VLSI Mentor
choosing.pl
use strict;
use warnings;
use v5.10;

my %errors = (alu => 0, fifo => 7, decoder => undef);

for my $block (qw(alu fifo decoder regfile)) {
    if (!exists $errors{$block}) {
        printf "%-8s : not run\n", $block;
    }
    elsif (!defined $errors{$block}) {
        printf "%-8s : ran, no count recorded\n", $block;
    }
    elsif ($errors{$block} == 0) {
        printf "%-8s : clean\n", $block;
    }
    else {
        printf "%-8s : %d error(s)\n", $block, $errors{$block};
    }
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
alu      : clean
fifo     : 7 error(s)
decoder  : ran, no count recorded
regfile  : not run

Four distinct outcomes from four distinct tests, in the order exists → defined → value. That ordering matters: testing defined first would merge "not run" into "no count recorded", and testing the value first would hit the undef case and warn.

Note the value test is == 0, not if (...). Once you know the value is defined, comparing it is clearer than relying on truth — and Chapter 3.5 is why it is == and not eq.

// is the defaulting form

Azvya Education Pvt. Ltd.VLSI Mentor
defaulting.pl
use strict;
use warnings;
use v5.10;

my %errors = (alu => 0);

printf "alu with // : %s\n", $errors{alu} // "missing";
printf "alu with || : %s\n", $errors{alu} || "missing";
printf "zzz with // : %s\n", $errors{zzz} // "missing";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
alu with // : 0
alu with || : missing
zzz with // : missing

// tests definedness, so the real zero survives. || tests truth, so it replaces the zero with the default — Chapter 3.4 §4's measurement, now with a hash element instead of a scalar.

For a hash of counts, // is the operator. // needs Perl 5.10 or later, as Chapter 3.4 established.

6. exists Works on Arrays Too

Azvya Education Pvt. Ltd.VLSI Mentor
array-exists.pl
use strict;
use warnings;

my @a = (1, undef, 3);

for my $i (0, 1, 2, 5) {
    printf "a[%d] : exists %-3s defined %s\n", $i,
        (exists $a[$i] ? "yes" : "no"),
        (defined $a[$i] ? "yes" : "no");
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
a[0] : exists yes defined yes
a[1] : exists yes defined no
a[2] : exists yes defined yes
a[5] : exists no  defined no

Position 1 holds undef — it exists and is not defined. Position 5 is past the end — it does not exist.

That makes exists the test that distinguishes Chapter 5.1 §5's silent out-of-range read from a legitimately undefined element. It is used far less on arrays than on hashes, because a position is rarely "missing" in a meaningful way — an array of four fields has positions 0 to 3 and that is that. The hash case is the one worth the habit.

7. Why This Matters in Engineering Work

A status report has three outcomes, not two. Clean, dirty, and never ran. A test that only distinguishes two of them will put one of the three in the wrong column, and §4 shows which: the clean ones get reported as unknown.

Zero is the good news and it is false. That one sentence is the whole chapter. Any construct reading a count as a boolean — if ($h{$k}), $h{$k} || $default, grep { $h{$_} } — treats a clean result as an absent one.

"Ran but produced nothing" is a real third state. A test that crashed before writing its count, a tool that emitted a row with an empty field. exists without defined catches it, and it deserves its own line in a report because it is an infrastructure problem rather than a design one.

And exists is the guard for a first-wins build. Chapter 6.3 §3's duplicate-row case had four possible policies; "keep the first" is $h{$k} = $v unless exists $h{$k}; — which works correctly even when the first value was 0 or undef, where a truth or definedness guard would not.

8. Common Mistakes

Putting a hash element straight into an if to test presence. It tests truth. A zero or empty value reads as absent.

Using defined when you mean exists. They differ for a key explicitly set to undef — a real state, as Chapter 6.3 §4 showed.

Assuming "0" and "00" behave alike. "0" is false; "00" is true.

Defaulting a count with ||. It replaces a measured zero. Use //.

Testing the value before checking defined. A comparison against undef warns.

Testing defined before exists when you need to distinguish "not run" from "no value". The order has to be exists, then defined, then the value.

Expecting a plain read to create a key. It does not; only nested access does, which is Chapter 7.9's subject.

9. Exercise

Exercise 1 — build the matrix. Reproduce §2's script with all seven states and print the three-column table. Then write one sentence for each row where the three tests disagree.

Exercise 2 — the "00" row. Test "0", "00", "0.0", " " and "0E0" for truth. Report which are true and name the chapter that lists Perl's false values.

Exercise 3 — the hazard. Build a hash with one block at 0 errors and one at 3, then report on three blocks — including one absent — using if ($h{$k}). Record which two blocks got identical output and explain why that is wrong.

Exercise 4 — fix it with exists. Rewrite Exercise 3 so all three blocks get distinct, correct reports. Confirm the clean block is reported as clean.

Exercise 5 — four outcomes. Reproduce §5's four-way if/elsif chain with a hash containing a zero, a non-zero, an undef and one absent key. Then move the defined test before the exists test and record which two outcomes merge.

Exercise 6 — // against ||. For a key whose value is 0, print the result of defaulting with // and with ||. Say which you would use for an error count and why in one clause.

Exercise 7 — first wins. Build a hash from four rows where one key repeats, keeping the first value rather than the last. Use exists as the guard, then confirm it still works when the first value is 0.

10. Summary

  • Three questions, not one: exists asks whether the key was ever set, defined whether it holds a real value, and if ($h{$k}) whether that value is true.
  • They disagree on four of seven states — see §2's matrix.
  • 0, "0" and "" exist and are defined and are false. "00" is true.
  • A key set to undef exists but is not defined; an absent key fails both.
  • if ($errors{$block}) reports a clean block exactly as it reports an untested one, and in this trade clean is the common case — so the bug fires on most rows.
  • Order the tests exists → defined → value when all three outcomes matter.
  • Default a count with //, never ||, or a measured zero is replaced.
  • A plain read does not create a key; only nested access does.
  • exists works on array elements too, distinguishing an undef element from one past the end.

Chapter 6.5 moves from asking about one key to walking through all of them — the two functions that give you every key and every value, and a third that keeps a hidden position marker and is easier to misuse than it looks.

Where this fits

Part of the Perl curriculum.