views:

93

answers:

2

I want to pass a lexical file handle to a subroutine using a named argument, but the following does not compile:

#!/usr/bin/perl -w
use strict;

my $log_fh;
my $logname = "my.log";

sub primitive {
   my ($fh, $m) = @_;
   print $fh $m;
}

sub sophisticated {
   my ($args) = @_;
   print $args->{m};
   print $args->{fh} $args->{m} ;
}

open $log_fh, ">", $logname;

print $log_fh "Today I learned ...\n";

primitive($log_fh,"... the old way works ...\n");

sophisticated({
   fh=>$log_fh, 
   m=>"... and the new way requires an intervention by SO.",
   });
close $log_fh;

The complaint is:

Scalar found where operator expected at ./lexical.file.handle.pl line 15, near
} $args"
(Missing operator before  $args?)

$ perl --version

This is perl, v5.10.1

It works O.K. when I use the primitive technique of passing arguments, and the named-argument hash technique works for the message portion, just not for the file handle portion. Do I need a new version of print ?

+13  A: 

When you've got a complex expression that returns a filehandle (like $args->{fh}) you'll need to disambiguate the syntax a bit by adding some extra curlies:

print { $args->{fh} } $args->{m};

This is due to the weird way the print operator is designed, with no comma between the filehandle and the list of stuff to print.

Alternatively, you could grab the filehandle out of your arguments hashref first, e.g.

my $fh = $args->{fh};
print $fh $args->{m};
friedo
Awesome! Thanks, friedo!
Thomas L Holaday
That's because you're using indirect object syntax with `print`. The odd thing is if you do `$args{fh}->print( 'blah' );` you have to `use IO::Handle;` or you get an error. But with the indirect notation there is no need. See http://perldoc.perl.org/perlobj.html#Indirect-Object-Syntax for more info.
daotoad
+4  A: 

friedo's answer covers your problem, but there's a stylistic issue I'd like to point out. You don't need to wrap everything in an anonymous hash to emulate named arguments. A hash initializer is just a list interpreted as key/value pairs. Passing such a list to a sub provides a cleaner syntax for the caller:

sub sophisticated {
   my %arg = @_;
   print $arg{m};
   print {$arg{fh}} $arg{m};
}

sophisticated(fh => $log_fh, m => "Hello, world!\n");
Michael Carman
This should have been a comment, not an answer.
fengshaun
@fengshaun: It's too big to (clearly) fit in a comment.
Michael Carman
There is one major advantage to using the anon hash for named parameters. If you accidentally pass an odd number of values then the warning will be in the calling code, not the called function.
Ven'Tatsu