In the recent discussion of ref($class)||$class, it was proposed that the idiom somehow makes it worse when a method is accidentally called as a function. I don't quite follow this, but quickly tried a workaround:
# instead of my $class = shift; my $self = bless {}, ref($class) || $class; # try my $self = bless {}, ref($_[0]) || @_
Note that || does properly propagate list context onto its right operand. The problem is that bless has a "$;$" prototype, so there isn't list context, there's scalar context instead. So calling it as a function results in a blessing into class "0" :)

open also suffers from this (it has a "*;$@" prototype). So if you want a sub to do some processing and then turn around and open a file passing through its parameters to open, you can't do:

sub foo { # some stuff open my $fh, @_; return $fh; }
Instead you need:
sub foo { # some stuff my $fh; if (@_ == 1) { open $fh, $_[0]; } else { open $fh, $_[0], $_[1], @_[2..$#_]; } return $fh; }
which IMO is real ugly (TM).

What bothers me most is that I can't think of any good reason *why* the optional args should have scalar context for either bless or open. Prototypes of "$@" and "*@" would be just as good in any case I can think of. I can't help but think that someone fell prey to what I call prototypitis; that is, using prototypes to describe what a function expects, rather than using them to control how a function is called.

Replies are listed 'Best First'.
Re: multi-arg bless (core==prototyped)
by tye (Sage) on Jul 13, 2004 at 03:24 UTC

    I guess you haven't noticed that the entire Perl complement of core functions suffer from "prototypitis". Some are "list ops" and don't suffer as much. But they all have prototypes (including prototypes not available to non-core functions) dictacting what they get passed (though some get lucky and mostly only affect how they get called, just because prototypes are rather blunt instruments).

    Unfortunately, there is too much chance for existing code to depend on this scalar context behavior so fixing it isn't simple (though a pragma might work -- I'd like to see such patches being attempted).

    - tye        

Re: multi-arg bless
by jeffa (Bishop) on Jul 13, 2004 at 18:20 UTC

    I have always considered this a non-problem, because i never use that ref($_[0]) mumbo jumbo. Consider the following code:

    my $foo = Foo->new1; print $foo->{bar}; # here is the problem, Jim #my $bar = $foo->new1; #print $bar->{bar}; my $baz = $foo->new2; print $baz->{bar}; package Foo; sub new1 { bless { bar => 'baz' }, $_[0] } sub new2 { bless { bar => 'baz' }, ref($_[0]) || $_[0] }
    The only reason why you need that ref($_[0]) mumbo jumbo is so the the commented out code will work. I never construct a new object from another ... i use Clone instead.

    As for the open stuff, i totally agree ... but if you do really need to abstract open, use IO::File

    my $fh = foo('foo.pl','r'); print while <$fh>; sub foo { IO::File->new(@_) }
    Again, i do not consider this much of problem because there are clean workarounds.

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: multi-arg bless
by rir (Vicar) on Jul 13, 2004 at 17:04 UTC
    The problem seems to me to be that:
    my $self = bless {}, ref($class) || $class;
    is improperly refactored as
    my $self = bless {}, ref($_[0]) || @_;
    when it should be
    my $self = bless {}, ref($_[0]) || $_[0];

    I am avoiding the arguments for and against the use of such dual nature constructors.

    Be well.

      The goal was to pass only one arg to bless when @_ is empty. In this case, $_[0] will be undef, and bless will give a warning that it is treated as an empty string and another warning that blessing to an empty string is treated as blessing into package "main" (whereas 1-arg bless would bless into the current package). Obviously there are ways to make it work; my point was just that a (to me, pointless) prototype interferes with one of the easiest.