Perl Best Practices

19.14. Enbugging

Be careful to preserve semantics when refactoring syntax.

The guidelines in this book are designed to improve the robustness, efficiency, and maintainability of your code. However, you need to take extra care when applying them retrospectively to the source of existing applications.

If you're rewriting existing code to bring it into line with the practices suggested here, be sure that you preserve the existing behaviour of the code through the changes you make. For example, if you have a loop such as:

for (@candidates) { next unless m/^Name: (.+?); $/; $target_name = $1 and last }

you might refactor it to:

# Find the first candidate with a valid Name: field... CANDIDATE: for my $candidate (@candidates) { # Extract the contents of the Name: field... my ($name) = $candidate =~ m/^Name: (.+?); $/xms; # ...or try elsewhere... next CANDIDATE if !defined $name; # If name found, save it and we're done... $target_name = $name; last CANDIDATE; }

However, adding the /xms (as recommended in Chapter 12) will alter the semantics of the pattern inside the regular expression. Specifically, it will change the meaning of ^, $, .+?, and the space character. Even though the pattern's syntax didn't change, its behaviour did. That's a particularly subtle way to break a piece of code.

In this case, you have to make sure that you apply all of the guidelines in Chapter 12, changing the pattern as well as the flags, so as to preserve the original behaviour:

# Find the first candidate with a valid Name: field... CANDIDATE: for my $candidate (@candidates) {

# Extract the Name: field... my ($name) = $candidate =~ m{\A Name: \s+ ([^\N]+) ; \s+ \n? \z}xms;

# ...or try elsewhere... next CANDIDATE if !defined $name;

# If name found, save it and we're done... $target_name = $name; last CANDIDATE; }

Good intentions don't prevent bad outcomes, especially when you're renovating existing code. Making obvious improvements can also introduce unobvious bugs. However, the recommendations in Chapter 18 can help you detect when virtue has become its own punishment. For example, having rewritten that statement, you could immediately rerun prove -r, whichassuming your test suite was sufficiently comprehensivewould highlight the unintended change in behaviour.

Категории