Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

questions about bless's second argument

by Special_K (Scribe)
on Nov 19, 2020 at 23:25 UTC ( #11123860=perlquestion: print w/replies, xml ) Need Help??

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

I am learning about Perl 5's object-oriented programming features and have a question about the "bless" keyword. Bless takes either one or two arguments: the first argument is a reference, and the second (optional) argument is the package to bless the referent into. If the second argument is omitted, it defaults to the current package. My question is: why/how would you ever need to specify a package other than the current package when using bless? Suppose I have the following constructor in Widget.pm:

package Widget; use strict; use warnings; sub new { my $class = shift(); my ($arguments) = @_; my $self = {}; bless $self; }

In this case, the Widget constructor will always be used to construct Widgets, so the second argument to bless can be omitted as it will default to Widget. Under what circumstances would you use one package's constructor to create objects in a different package, thus requiring the second argument to bless?

Replies are listed 'Best First'.
Re: questions about bless's second argument
by GrandFather (Sage) on Nov 19, 2020 at 23:52 UTC

    See perlobj for the full story. The short version is that the second argument is the 'class' of the instance being created. If you aren't using inheritance then it's not very interesting. However consider:

    use strict; use warnings; package PrintMe; sub AsStr { my ($self) = @_; return $self->{str}; }; sub new { my ($class, $str) = @_; return bless {str => $str}, $class; } package PrintMeToo; use base 'PrintMe'; sub AsStr { my ($self) = @_; return 'Me too: ' . $self->SUPER::AsStr(); }; package main; my $meToo = PrintMeToo->new('This string'); print $meToo->AsStr();

    Prints:

    Me too: This string

    PrintMeToo is derived from PrintMe and inherits the constructor 'new' from PrintMe. Calling the constructor (my $meToo = PrintMeToo->new('This string');) gets the inherited PrintMe constructor called but creates a PrintMeToo instance.

    Calling AsStr on the PrintMeToo instance gets the PrintMeToo version of AsStr, but that version calls up to the base class (PrintMe) to get the result from the base class AsStr to concatenate to the text provided by PrintMeToo's AsStr.

    Bottom line is, always use the two parameter version of bless and always use the first parameter to the constructor sub to provide the class name.

    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
Re: questions about bless's second argument
by tybalt89 (Prior) on Nov 20, 2020 at 00:13 UTC

    Bless is not just for constructors!

    The sample code in this post Re: Use cases for 'sub Pckg::func { }' ? shows a parser for arithmetic expressions that builds a parse tree (AbstractSyntaxTree) consisting of anonymous arrays blessed into a package named for the operation it should perform. I can then get the value of the entire tree just by sending the method ->value to the head of the tree and rely on perl's polymorphism to execute the value sub in the correct package.

    The sub "node" does the blessing into any requested package.

Re: questions about bless's second argument
by Fletch (Chancellor) on Nov 19, 2020 at 23:54 UTC

    One possible usage: imagine if you had some sort of factory method and wanted to create subclasses on the fly. Handwavy terrible example just what you could do with it (use a real metaprogramming module instead):

    ## In Widget sub make_color_widget { my $class = shift; my $new_color = shift; my( @widget_args ) = @_; { no strict refs; @{ qq{Widget::${new_color}::ISA } = (qw( Widget ) ); } my $self = {@widget_args}; return bless $self, qq{Widget::$new_color}; } sub my_color { my $self = shift; return ( (ref $self) =~ m{^Widget::(.*)} )[0]; }

    Then you could do something like:

    $ perl -I . -MWidget -de 0 Loading DB routines from perl5db.pl version 1.57 Editor support available. Enter h or 'h h' for help, or 'man perldebug' for more help. main::(-e:1): 0 DB<1> $red = Widget->make_color_widget( red => () ); DB<2> $blue = Widget->make_color_widget( blue => () ); DB<3> x $red->my_color 0 'red' DB<4> x $blue->my_color 0 'blue'

    Rather than cheating and scraping from the instance's class name, you could use that as a key to lookup whatever in a hash keyed by the class name. Or you could (dynamically) generate code and populate subs in the new class' package namespace (*{"Widget::${new_color}::frobulate} = sub { ... }

    Edit: a possible usage other than the obvious that's how subclasing works, of course. (derp)

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: questions about bless's second argument
by tobyink (Canon) on Nov 20, 2020 at 12:02 UTC

    You should always use the two argument form.

    package Widget; use strict; use warnings; sub new { my ( $class, @args ) = ( shift, @_ ); my $self = {}; bless( $self, $class ); }

    I shall anticipate your next question: But $class is the current package, right? So isn't this the same as a one argument bless?

    No, it's different. Consider:

    { package Widget::Special; use parent 'Widget'; # We don't write a sub called `new` and rely on the parent class f +or that. } my $special = Widget::Special->new;

    Now $class is "Widget::Special", so the two-argument form of bless does the right thing. The one-argument form of bless would return a plain "Widget" object from Widget::Special->new.

    Always, always, always use the two-argument form of bless. One-argument bless needs to go and painfully die in a stinking hole of rancid rotting trash.

    I hope that helps. :)

    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: questions about bless's second argument
by perlfan (Vicar) on Nov 20, 2020 at 20:01 UTC
    Welcome! My preferred idiom or pattern looks like this,
    package Widget; use strict; use warnings; # generic constructor, allows for any keys to be passed like, # my $widget = Widget->new(field1 => "value 1", field2 => "value 2", e +tc => "etc..."); sub new { my ($pkg, %self) = @_; # can validate parameters here... (I like Validate::Tiny) # technically it's a "package", also the 2nd argument to # bless is a string, literally just the "package" name ('Widget') return bless \%self, $pkg; } 1; #<-- note

    I highly recommend looking at the following:

    My standard advice is to stay away from any OO perl systems that promise the m00n until you understand how Perl makes all this happen. Conway's, Object Orient Perl is a must peruse.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://11123860]
Approved by GrandFather
Front-paged by kcott
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (3)
As of 2020-12-03 08:39 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    How often do you use taint mode?





    Results (53 votes). Check out past polls.

    Notices?