Lotus1 has asked for the wisdom of the Perl Monks concerning the following question:

While updating an old program I noticed I had passed a list from die() to a scalar parameter in a subroutine. Even though the list only contained one element I assumed this was a bad practice. Out of curiosity I tested adding a second element and was surprised that the next parameter didn't get overwritten because the @_ elements were squashed into a single string. This seems useful but is it safe to rely on something like this?

use strict; use warnings; my @msg = ( "This is line one.", "Line two.\n" ); die @msg; sub BEGIN { $SIG{__DIE__} = sub { my @log_files = qw( error.log ftp.log ); send_status_email( 'me@work.com', 'script error', @_, \@log_fi +les ); }; } sub send_status_email { my ($to_address, $status, $message, $ar_attachments ) = @_; print "To: $to_address\n"; print "Status: $status\n"; print "Message <$message>\n"; foreach ( @$ar_attachments ) { print "ar: $_\n"; } print "***************** sub finished\n"; } __END__ **Output** To: me@work.com Status: script error Message <This is line one.Line two. > ar: error.log ar: ftp.log ***************** sub finished This is line one.Line two.

Replies are listed 'Best First'.
Re: Why are list items passed to die() combined into a string?
by kennethk (Abbot) on Jan 13, 2015 at 17:13 UTC
    For some somewhat more useful discussion of this expected behavior (I find the docs poor on this one), see Re: Perl's Warn and Die Signals and its links.

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

      Thanks, that verifies what I see. I had read the article that node was in response to but I didn't make it down that far. Another node states that die() is just doing what print() does and concatenates its inputs for display.

      Update:

      I found the following at General Variables in the %SIG section:

      The routine indicated by $SIG{__DIE__} is called when a fatal exception is about to be thrown. The error message is passed as the first argument.
        Thing to note: neither $" or $, will impact the concatenation seen by $SIG{__DIE__}(e.g. join '', @_). This contrasts print, where print "@_" or print @_ can have their behavior impacted.

        I far prefer overriding die to working with $SIG{__DIE__} myself, since the latter's behavior isn't what I usually actually want to impact.


        #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

Re: Why are list items passed to die() combined into a string?
by roboticus (Chancellor) on Jan 13, 2015 at 17:01 UTC

    Lotus1:

    If you check the docs (perldoc -f die), you'll see that's the intended behavior. It's not "bad practice" to pass in a list.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

      roboticus wrote:

      If you check the docs (perldoc -f die), you'll see that's the intended behavior. ...

      Actually, I did read the perldoc for die and the only thing I saw about stringification was this:

      Because Perl stringifies uncaught exception messages before display, you'll probably want to overload stringification operations on exception objects.

      That isn't exactly obvious that it has anything to do with @_ or that die() is doing anything or what the intended behavior is with respect to @_.

      ... It's not "bad practice" to pass in a list.

      The reason I asked about "bad practice" is shown in the example below. Passing a list to a parameter will cause the second element of the list to go to the next parameter. In the original example it appears that @_ is always just a single item but this is confusing for anyone who maintains or reuses this code. I guess I just answered my own question; This is a very bad practice.

      use strict; use warnings; my @array1 = ( "this is a line\n", "this is a second line"); my @array2 = qw( one two three); sub1 ( @array1 ); sub sub1 { send_status_email('xxx@yy.com', 'subject',@_, \@array2); } sub send_status_email { my ($to_address, $status, $message, $ar_attachments ) = @_; print "To: $to_address\n"; print "Status: $status\n"; print "Message <$message>\n"; foreach ( @$ar_attachments ) { print "ar: $_\n"; } print "***************** sub finished\n"; } __END__ ** output ** To: xxx@yy.com Status: subject Message <this is a line > Can't use string ("this is a second line") as an ARRAY ref while "stri +ct refs" in use at C:\usr\dms\perl\test_sub_array.pl line 20.

        UPDATE: Ignore this... I'm not sure how I missed that you were pointing out the bad example of how not to use @_.

        You are passing @_ as the third argument to send_status_email followed by \@array2. Since @_ contains two items and send_status_email is expecting a scalar for the third argument, the second item in @_ is going into $ar_attachments. Here is one way to fix that:

        use strict; use warnings; my @array1 = ( "this is a line\n", "this is a second line"); my @array2 = qw( one two three); sub1 ( @array1 ); sub sub1 { send_status_email( 'xxx@yy.com', 'subject', \@_, \@array2); } sub send_status_email { my ($to_address, $status, $message, $ar_attachments ) = @_; print "To: $to_address\n"; print "Status: $status\n"; my $body = join '', @$message; print "Message <$body>\n"; foreach ( @$ar_attachments ) { print "ar: $_\n"; } print "***************** sub finished\n"; } __END__ To: xxx@yy.com Status: subject Message <this is a line this is a second line> ar: one ar: two ar: three ***************** sub finished
Re: Why are list items passed to die() combined into a string?
by locked_user sundialsvc4 (Abbot) on Jan 13, 2015 at 18:11 UTC

    Obviously, the intent of this feature was to allow a single string to be conveniently constructed from multiple arguments, as the perldoc -f die output demonstrates.   But it is also possible to pass an object.   Quoting again from the perldoc:

    You can also call "die" with a reference argument, and if this is trapped within an "eval", $@ contains that reference.   This permits more elaborate exception handling using objects that maintain arbitrary state about the exception.   Such a scheme is sometimes preferable to matching particular string values of $@ with regular expressions. [...]

    Packages such as Exception::Class, among many others, make good use of this feature.   I find it particularly useful to be able to catch an exception, and then by use of isa() and so-forth to easily obtain contextual information about exactly what went wrong, in addition to what else I may learn from querying that object’s properties and methods.   (This is also an excellent way to allow inner exception-handlers to determine whether the exception they have caught is one [of a class ...] that they should handle, or one that they should re-throw.)

    Sometimes, “throwing an exception (object ...)” is a great way to “bail out” of perfectly ordinary situations.   The Python language, of course, takes this idea rather to an extreme . . .

    When invoking code that might throw a string-based exception, I will catch that exception in nearby containing-code, and then construct an object containing (among other things) the caught string, and re-throw that object, so that, throughout my entire application, every exception that makes its way up to outer-level andlers can be counted-on to be an object of some kind.   (Although even these handlers must of course be also prepared to catch a string, which of course would be recognized to be an “entirely unanticipated, out-of-the-blue” error condition, because it should have been caught and object-ified, but it wasn’t.)