Listen Print

The Perl You Need To Know - Part 2

by Stas Bekman
May 07, 2002

Introduction

In this article, we continue to talk about the essential Perl basics that you should know before starting to program for mod_perl.

Tracing Warnings Reports

Sometimes it's hard to understand what a warning is complaining about. You see the source code, but you cannot understand why some specific snippet produces that warning. The mystery often results from the fact that the code can be called from different places if it's located inside a subroutine.

Here is an example:


  warnings.pl
  -----------
  #!/usr/bin/perl -w

  use strict;

  correct();
  incorrect();

  sub correct{
    print_value("Perl");
  }

  sub incorrect{
    print_value();
  }

  sub print_value{
    my $var = shift;
    print "My value is $var\n";
  }

In the code above, print_value() prints the passed value. Subroutine correct() passes the value to print, but in subroutine incorrect() we forgot to pass it. When we run the script:


  % ./warnings.pl

we get the warning:


  Use of uninitialized value at ./warnings.pl line 16.

Perl complains about an undefined variable $var at the line that attempts to print its value:


  print "My value is $var\n";

But how do we know why it is undefined? The reason here obviously is that the calling function didn't pass the argument. But how do we know who was the caller? In our example, there are two possible callers, in the general case there can be many of them, perhaps located in other files.

We can use the caller() function, which tells who has called us, but even that might not be enough: It's possible to have a longer sequence of called subroutines, and not just two. For example, here it is sub third() which is at fault, and putting sub caller() in sub second() would not help:


  sub third{
    second();
  }
  sub second{
    my $var = shift;
    first($var);
  }
  sub first{
    my $var = shift;
   print "Var = $var\n"
  }

The solution is quite simple. What we need is a full calls stack trace to the call that triggered the warning.

The Carp module comes to our aid with its cluck() function. Let's modify the script by adding a couple of lines. The rest of the script is unchanged.


  warnings2.pl
  -----------
  #!/usr/bin/perl -w

  use strict;
  use Carp ();
  local $SIG{__WARN__} = \&Carp::cluck;

  correct();
  incorrect();

  sub correct{
    print_value("Perl");
  }

  sub incorrect{
    print_value();
  }

  sub print_value{
    my $var = shift;
    print "My value is $var\n";
  }

Now when we execute it, we see:


  Use of uninitialized value at ./warnings2.pl line 19.
    main::print_value() called at ./warnings2.pl line 14
    main::incorrect() called at ./warnings2.pl line 7

Take a moment to understand the calls stack trace. The deepest calls are printed first. So the second line tells us that the warning was triggered in print_value(); the third, that print_value() was called by subroutine, incorrect().


  script => incorrect() => print_value()

O'Reilly Open Source Convention -- July 22-26, San Diego, CA.

From the Frontiers of Research to the Heart of the Enterprise

mod_perl 2.0, the Next Generation Stas Bekman will provide an overview of what's new in mod_perl 2.0 and what else is planned for the future in his talk at the upcoming O'Reilly Open Source Convention, this July 22-26, in San Diego.

We go into incorrect() and indeed see that we forgot to pass the variable. Of course, when you write a subroutine such as print_value, it would be a good idea to check the passed arguments before starting execution. We omitted that step to contrive an easily debugged example.

Sure, you say, I could find that problem by simple inspection of the code!

Well, you're right. But I promise you that your task would be quite complicated and time consuming if your code has some thousands of lines. In addition, under mod_perl, certain uses of the eval operator and ``here documents'' are known to throw off Perl's line numbering, so the messages reporting warnings and errors can have incorrect line numbers. This can be easily fixed by helping compiler with #line directive. If you put the following at the beginning of the line in your script:


 #line 125

then it will tell the compiler that the next line is number 125 for reporting needs. Of course, the rest of the lines would be adapted as well.

Getting the trace helps a lot.

my() Scoped Variable in Nested Subroutines

Before we proceed let's make the assumption that we want to develop the code under the strict pragma. We will use lexically scoped variables (with help of the my() operator) whenever it's possible.

The Poison

Let's look at this code:


  nested.pl
  -----------
  #!/usr/bin/perl

  use strict;

  sub print_power_of_2 {
    my $x = shift;

    sub power_of_2 {
      return $x ** 2; 
    }

    my $result = power_of_2();
    print "$x^2 = $result\n";
  }

  print_power_of_2(5);
  print_power_of_2(6);

Don't let the weird subroutine names fool you, the print_power_of_2() subroutine should print the square of the number passed to it. Let's run the code and see whether it works:


  % ./nested.pl

  5^2 = 25
  6^2 = 25

Ouch, something is wrong. May be there is a bug in Perl and it doesn't work correctly with the number 6? Let's try again using 5 and 7:


  print_power_of_2(5);
  print_power_of_2(7);

And run it:


  % ./nested.pl

  5^2 = 25
  7^2 = 25

Wow, does it works only for 5? How about using 3 and 5:


  print_power_of_2(3);
  print_power_of_2(5);

and the result is:


  % ./nested.pl

  3^2 = 9
  5^2 = 9

Now we start to understand -- only the first call to the print_power_of_2() function works correctly. This makes us think that our code has some kind of memory for the results of the first execution, or it ignores the arguments in subsequent executions.

The Diagnosis

Let's follow the guidelines and use the -w flag:


  #!/usr/bin/perl -w

Under Perl version 5.6.0+ we use the warnings pragma:


  #!/usr/bin/perl
  use warnings;

Now execute the code:


  % ./nested.pl

  Variable "$x" will not stay shared at ./nested.pl line 9.
  5^2 = 25
  6^2 = 25

We have never seen such a warning message before and we don't quite understand what it means. The diagnostics pragma will certainly help us. Let's prepend this pragma before the strict pragma in our code:


  #!/usr/bin/perl -w

  use diagnostics;
  use strict;

And execute it:


  % ./nested.pl

Variable "$x" will not stay shared at ./nested.pl line 10 (#1)

(W) An inner (nested) named subroutine is referencing a lexical variable defined in an outer subroutine.

When the inner subroutine is called, it will probably see the value of the outer subroutine's variable as it was before and during the *first* call to the outer subroutine; in this case, after the first call to the outer subroutine is complete, the inner and outer subroutines will no longer share a common value for the variable. In other words, the variable will no longer be shared.

Furthermore, if the outer subroutine is anonymous and references a lexical variable outside itself, then the outer and inner subroutines will never share the given variable.

This problem can usually be solved by making the inner subroutine anonymous, using the sub {} syntax. When inner anonymous subs that reference variables in outer subroutines are called or referenced, they are automatically rebound to the current values of such variables.


  5^2 = 25
  6^2 = 25

Well, now everything is clear. We have the inner subroutine power_of_2() and the outer subroutine print_power_of_2() in our code.

When the inner power_of_2() subroutine is called for the first time, it sees the value of the outer print_power_of_2() subroutine's $x variable. On subsequent calls the inner subroutine's $x variable won't be updated, no matter what new values are given to $x in the outer subroutine. There are two copies of the $x variable, no longer a single one shared by the two routines.

Pages: 1, 2, 3

Next Pagearrow





Contact Us | Advertise with Us | Privacy Policy | Press Center | Jobs | Submissions Guidelines

Copyright © 2000-2008 O’Reilly Media, Inc. All Rights Reserved. | (707) 827-7000 / (800) 998-9938
All trademarks and registered trademarks appearing on the O'Reilly Network are the property of their respective owners.

For problems or assistance with this site, email