Just another Perl shrine | |
PerlMonks |
Use strict warnings and diagnostics or dieby tachyon (Chancellor) |
on Jun 11, 2001 at 23:37 UTC ( [id://87628]=perlmeditation: print w/replies, xml ) | Need Help?? |
Alternate title: Read this if you want to cut your development time in half!Note this article was written as a resource for the New Monks Info Page node in response to some wonderful feedback from fellow monks. It is designed as an easy read for new monks who may need easing into the world of strict and warnings. I have posted to meditations so I can edit it in response to monk feedback as has happened with previous nodes. I guess tutorials or Q&A will be its final resting place. Perl is a wonderful language. As an interpreted language your first program can be as simple as this old classic: print "Hello world\n"; When you compare this to the same program in c which also needs to be compiled before it will run the attraction seems obvious. #include <stdio.h> int main() { printf("Hello World!\n"); return 0; } As soon as you move past the trivial and start coding longer programs you rapidly discover the concept of the bug. All newer distributions of the Perl language include a number of pragmas to help you with the inevitable debugging. Debugging might be defined as the final step required to transfer your idea of what you want a computer to do into a set of instructions that the computer understands (and which it can execute) to make your idea a practical reality. Sometimes some rather more crusty descriptions come to mind. Perl has three pragmas specifically designed to make your life easier: use strict; use warnings; use diagnostics; If you don't understand the meaning of the word pragma don't worry, it doesn't matter, it is just a jargon term that encompasses these three features (amongst others). We will cover each pragma in turn, clearly stating how and why you should be using it. The benefits far outweigh the short learning curve. Note that for the short, quick and dirty solutions that Perl does so easily you don't need them but as your code gets longer and/or in a production environment they are invaluable. "If you intend on building production-level code, then "use strict" and "use warnings" should be disregarded only if you know exactly why you are doing it" - dragonchild Perl has a great help thy neighbour culture, but you will quickly discover the response you receive to any request for help (regardless of the forum) will be far better if you have tried to help yourself. Using the strict, warnings and diagnostics pragmas is generally deemed to fall into this self help category. If you are not using these pragmas the responses you receive to a help request will vary from the helpful "OK, here is your problem on line xxx" to the more pointed "Don't ask a person to do a machine's work" to the succinct RTFM. Don't expect a person to do a machine's work can be explained thus: "Perl provides a lot of help to debug your program and get it working via the strict, warnings and diagnostics pragmas. Perl is very good at this task. If you can't be bothered doing this basic thing then why should I expend my valuable unpaid time and effort to help you?" Whilst the delivery of this response may vary from the gentle urging to the less subtle FOAD it is still very practical, and based on thousands of years of combined experience. Once you get in the habit of using these pragmas you will find Perl isolates problems automatically for you and can reduce development time by half or more (given that most of the time spent developing code is spent debugging). So let's get down to it! Bondage and discipline - use strict;To activate the strict pragma we add the line use strict; just below the shebang line.#!/usr/bin/perl use strict;Use strict actually consists of these three separate pragmas: use strict 'vars'; use strict 'refs'; use strict 'subs'; Each of these does a different thing as we will see. You *can* activate each of these separately, but most programmers prefer to activate all three with a simple use strict;. You may deactivate a pragma using: no strict; # deactivates vars, refs and subs; no strict 'vars'; # deactivates use strict 'vars'; no strict 'refs'; # deactivates use strict 'refs'; no strict 'subs'; # deactivates use strict 'subs'; OK so now we know how to turn them on and off what do they actually do? use strict 'vars';By far the most useful in general coding is the use strict 'vars'; pragma. When this is active Perl complains if you try to use a variable that you have not previously declared. $foo = 'bar'; # OK use strict 'vars'; $foo = 'bar'; # Triggers an error If we try to run the second example we will get an error like: Global symbol "$foo" requires explicit package name at script line 2. Execution of script aborted due to compilation errors. Fantastic you say, my simple program is now broken, and for what. For your sanity that's why. Consider this slightly longer (silly) example that counts down from ten then calls a sub liftoff. The liftoff sub is designed to count the number of liftoffs: $count = 10; while ($count > 0) { print "$count "; $count--; liftoff() if $count == 0; } sub liftoff { $count++; # count liftoffs print "This is liftoff $count\n"; } Perfectly valid code, but if these two snippets were separated by other code the fact that this is an infinite loop might be harder to spot, especially without the print statements. The problem occurs because of the global variable $count, which is changed within the liftoff sub. As a result the while loop never exits, and repeatedly calls the liftoff sub. Whilst this is very simplified the fact is that common variables like $count, $line, $data, $i, $j etc are often used many times within a program. If we do not localise them to where they are used some very nasty bugs can arise. These bugs can often be hard to find. Over time programmers have come to the realisation that global variables are in general a bad idea. A variables "scope" defines where it can be seen in the program - generally the narrower the better. A global variable can be seen and modified everywhere. A lexically scoped variable can only be seen within its lexical scope. To fix our errant liftoff sub we localise the $count variables to where the are needed via a "my" directive. my $count = 10; while ($count > 0) { print "$count "; $count--; &liftoff() if $count == 0; } { # scope our variable $count via a closure my $count; sub liftoff { $count++; # count liftoffs print "This is liftoff $count\n"; } } liftoff(); liftoff(); liftoff(); liftoff(); Now that we have localised our $count variables this prints: 10 9 8 7 6 5 4 3 2 1 This is liftoff 1 This is liftoff 2 This is liftoff 3 This is liftoff 4 This is liftoff 5 So as desired each time the liftoff sub is called it increments our counter. The lexical scope of a variable declared via my is most simply explained by example: my $count = 10; print "$count\n"; # $count is 10 here { my $count = 5; print "$count\n"; # $count is 5 here } print "$count\n"; # $count is 10 here again Astute readers will note that because we define $count at the top of this code it is effectively a global variable. This is quite true in a limited sense and often how programmers initialy deal with pacifying use strict. In this case we have a $count with a wide scope and another *different* $count with a narrow scope. Talk about have your cake and eat it to! In simple terms a lexical scope is the area from a opening curly to the corresponding closing curly. If you declare a variable with my within a lexical scope it only exists within the innermost lexical scope. Outside this scope it does not exist. For example: { my $count = 10; print "$count\n"; } print "\$count is zero or undefined!\n" unless $count; Prints: 10 $count is zero or undefined! For an extensive expert discussion of scoping variables see Coping with Scoping and Seven Useful Uses of Local by Mark-Jason Dominus. use strict 'refs';The use strict 'refs' pragma prevents you from using symbolic references. References are a very useful but somewhat advanced technique. You can write plenty of useful code without understanding them but eventually you will discover how they can make life much easier. Here are two trivial examples of references (sorry that they do not make the case for using references obvious): my $foo = "bar"; my $bar = "foo"; my $ref = \$foo; # hard reference print $$ref; # prints 'bar' # symbolic reference print $$bar; # also prints 'bar' So what gives? In the first case we create a hard reference to the variable $foo. We then print the value in $foo by dereferencing it via the $$ construct. In the second case what happens is that because $bar is not a reference when we try to deference it Perl presumes it to be a symbolic reference to a variable whose name is stored in $bar. As $bar contains the string 'foo' perl looks for the variable $foo and then prints out the value stored in this variable. We have in effect used a variable to store a variable name and then accessed the contents of that variable via the symbolic reference. Excellent you say, I wanted to know how to do that. Trust me it is a very bad idea. Besides making your code rather difficult to understand it can lead to some very difficult debugging. For a full discussion on why this is a really bad idea see Why it's stupid to use a variable as a variable name by one of my favourite guru's Mark-Jason Dominus again. Getting back to our example had the use strict 'refs' pragma been active then Perl would have let us know about this dangerous problem via a message along the lines: Can't use string ("foo") as a SCALAR ref while "strict refs" in use at test.pl line 12. use strict 'subs';In Perl you can call a subroutine in three ways: mysub; mysub(); &mysub; You can use the first form because perl assumes any bareword (ie not a key word and not quoted) must refer to a user defined subroutine. This is fine but what if you have a typo: sub my_sub { print "Hello World\n"; } mysub; This fails to print the expected "Hello World" because of the typo. Normally it would compile and run just fine, it just wouldn't work. However under use strict 'subs' perl will let us know we have a problem with a message like: Bareword "mysub" not allowed while "strict subs" in use at test.pl line 5. Execution of test.pl aborted due to compilation errors. Baby help me please - the use warnings pragmaTo activate the use warnings pragma you can do either of the following: #!/usr/bin/perl -w use warnings; # only on Perl 5.6.0 and greater Under Perl 5.6.0 and higher you can deactivate warnings (lexically scoped) like this: no warnings; First please note that "use warnings;" and "no warnings;" require Perl 5.6.0 or greater. Many people do not yet use this version so for maximum portability "-w" is preferred. Even though Win32 systems do not understand the concept of the shebang line Win32 Perl will look at the shebang and activate warnings if the "-w" flag is present so this works cross platform. For a full discussion see use warnings vs. perl -w. For Win32 users on *nix the operating system will examine the first two bytes of an executable file for the "#!" sequence and if found execute the file using the executable found on the path that follows the "#!". Unlike strict when perl generates warnings it does not abort running your code, but it can fill up your screen :-) Here is a simple example of how warnings can *really* help: $input_received = <STDIN>; exit if $input_recieved =~ m/exit/i; print "Looks like you want to continue!\n"; Three lines - what could be easier. We get some input from STDIN and exit if the user types exit. Otherwise we print something. When we run the code we find that regardless of what we type it prints. Why no exit?? Lets add warnings and see what happens: #!/usr/bin/perl -w my $input_received = <STDIN>; exit if $input_recieved =~ m/exit/i; print "Looks like you want to continue!\n"; Name "main::input_received" used only once: possible typo at test.pl line 3. Name "main::input_recieved" used only once: possible typo at test.pl line 4. test.pl syntax OK So we have a syntax error, fix that and we are off and running. If we were using strict the code would not have run in the first place but that's another story. Typos like this can be hard for humans to spot but perl does it in a jiffy. Use of a what in a where like how? - use diagnosticsWhen you first start to use warnings some of the messages appear quite cryptic. Don't worry, the "use diagnostics;" pragma has been designed to help. When this is active the warnings generated by "-w" or "use warnings;" are expanded greatly. You will probably only need to use diagnostics for a few weeks as you soon become familiar with all the messages! To finish off, here is another example: my @stuff = qw(1 2 3 4 5 6 7 8 9 10); print "@stuff" unless $stuff[10] == 5; If you run this code it runs OK, or does it? Sure it prints the array but there is a subtle problem. $stuff[10] does not exist! Perl is creating it for us on the fly. Use warnings catches this subtle trap but if we add the use diagnostics; pragma we will get a blow by blow description of our sins. use warnings; use diagnostics; my @stuff = qw(1 2 3 4 5 6 7 8 9 10); print "@stuff" unless $stuff[10] == 5; With warnings on the error is caught and with diagnostics on it is explained in detail. Use of uninitialized value in numeric eq (==) at test.pl line 4 (#1) (W uninitialized) An undefined value was used as if it were already defined. It was interpreted as a "" or a 0, but maybe it was a mistake. To suppress this warning assign a defined value to your variables. The Bad NewsSo now that I have waxed lyrically about the merits of these three pragmas it is time to give you the bad news. Fortunately there is not much. Learning CurveAs with anything new it takes a while to get used to the strictures of these pragmas. The overwhelming majority of Perl coders appreciate the benefits, but there is a learning curve. Use diagnostics has no learning curve - it is just to help you understand the warnings generated. The warnings are fairly self explanatory (especially with use diagnostics) and after a while you grow very used to all the free help the offer, especially on the typo front. Use strict requires some understanding of scoping your vars. I have already given you links to some of the best articles available on this topic so you have some good help available. Initially using strict is a pain. The pain is short and the benefits long so I encourage you to persevere. You will be over the hump in no time. CGIFor CGI scripts to work you need to output a header. Typically you will have: print "Content-type: text/html\n\n"; occuring early in your script. Errors from warnings and strict will generally be output before this resulting in a 500 internal server error due to malformed headers. I suggest running your script from the command line first to weed out the warnings and or adding: use CGI::Carp 'fatalsToBrowser'; Note you want to comment this out of the production code to avoid compromising security. If you have reached here you are already well on the road to Perl nirvana. I hope you enjoy the journey. tachyon
Back to
Meditations
|
|