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

Greetings fellow Monks. It's been a few weeks. I'm happy to report I'm making good progress on a fairly extensive project of mine thanks to all the kind help found here.

So one thing that is annoying me right now is the process of populating scalars with object properties to make code more readable. For example:

sub function { my $self = shift; my $val1 = $self->get_value1; my $val2 = $self->get_value2; my $val3 = $self->get_value3; print "Values: $val1, $val2, $val3"; }

For me, I find transferring object properties to scalars is less error prone and makes code easier to read than something like: print "Values: " . $self->get_value1 . ', ' . $self->get_value2 . ', ' . ', ' . $self->get_value3"; or printf "Values: %s, %s, %s", $self->get_value1, $self->get_value2, $self->get_value3";

I know it's possible to do print "${\$self->get_value}" but that is just god awful.

Using scalars is also useful if I have to use a property several times in the same block of code or if there are many object properties to keep track of. It saves a lot of typing and, again, makes the code easier to read. But it still can be super annoying to create these scalars. I'm wondering if there might be some useful trick out there that will spare me the oh so tedium of creating scalars for my object properties. Thanks.

$PM = "Perl Monk's";
$MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate";
$nysus = $PM . ' ' . $MCF;
Click here if you love Perl Monks

Replies are listed 'Best First'.
Re: Method for reducing tedium of transferring object properties to scalars
by haukex (Archbishop) on Apr 21, 2017 at 17:02 UTC

    In Perl, of course it's possible, but I'd say it's not a best practice. Do you really want to have a bunch of scalars floating around? It kind of goes against the idea of keeping your properties all neatly tied up in objects. See How can I use a variable as a variable name? but more importantly Why it's stupid to 'use a variable as a variable name'.

    So how many values are there that you want to access, and how often are you interpolating them into strings? Personally, I don't mind print "Foo=", $obj->get_foo, "\n";, consider getting used to it for simple cases. Otherwise, a few ideas:

    • Use printf+map: printf "foo=%s, bar=%s, quz=%s\n", map {$obj->${\"get_$_"}} qw/foo bar quz/;
    • Use a hash: my %h = map {$_=>$obj->${\"get_$_"}} qw/foo bar quz/; print "foo=$h{foo}, bar=$h{bar}, quz=$h{quz}\n";
    • Use a proper templating engine.

      Well, using it for interpolation is just one example. Let's say I have to use a property over and over. It gets pretty tiresome to keep typing out $self->get_value1 especially if it has a long property name. Also, I find $self-> adds visual clutter and makes the code harder to read in general. Creating these scalars does feel a bit dirty, though. I would like to get away from it. Maybe I should concentrate on making more regular use of my editor's autocomplete feature and just live with the uglier code.

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate";
      $nysus = $PM . ' ' . $MCF;
      Click here if you love Perl Monks

        Let's say I have to use a property over and over.

        I should have made this a little more clear: If we're just talking about one property copied into one scalar that gets used many times, or maybe even two or three properties/scalars, then doing so is probably still acceptable. But as soon as you do this more than that, then I'd start considering it "too much".

        Maybe I should concentrate on making more regular use of my editor's autocomplete feature and just live with the uglier code.

        I coded in Java for many years, where AbstractClassNameImplementationFactory.withReallyLongAndDescripiveMethodNames() are common - this seems horrible to some, but once you get good at using the editor's autocomplete, you hardly notice this stuff anymore. The same can apply in Perl, if you look at it the right way, i.e. reconsider your notion of "ugly" - $self->method_name (and the fact this doesn't interpolate) is just part of Perl's OO style, if you accept Perl's OO then consider accepting that part too :-)

        Also, there are other things you can do, for example in Perl it's very common to use $obj->prop and $obj->prop("newval") instead of $obj->get_prop() and $obj->set_prop("newval"), respectively. And it's also possible to shorten $self to $s or similar, so instead of $self->get_foo(), now you've got $s->foo, already a lot shorter.

Re: Method for reducing tedium of transferring object properties to scalars
by Eily (Monsignor) on Apr 21, 2017 at 16:50 UTC

    In your simple case of printing several values with a constant separator, there's either join, or $,:

    print "Values: ", join ", ", $self->get_value1, $self->get_value2, $se +lf->get_value3"; print "\nValues: "; { local $, = ", "; print $self->get_value1, $self->get_value2, $self->get_value3"; print "\nValues: ".$self->get_value1, $self->get_value2, $self->get_ +value3"; # notice the . after "\nValues: " }
    All three of those solutions have their downside though.

    For the most general case of printing method calls inside double quotes, all I can think of is using a tied hash to call the methods when accessing it. It works but it kind of defeats the purpose of writing accessor methods in the first place:

    use v5.14; use strict; use warnings; package Test { sub new { my $scalar = $_[1]; bless \$scalar, shift } sub value { ${ $_[0] } = $_[1] if @_ > 1; ${ $_[0] } } } package CallerHash { sub TIEHASH { return bless \$_[1], $_[0]; } sub FETCH { my $obj = ${ +shift }; my $method = shift; return $obj->$method(); } sub STORE { my $obj = ${ +shift }; my $method = shift; $obj->$method(@_); } } my $obj = Test->new(4); tie my %objAsHash, CallerHash => $obj; say "$objAsHash{value}"; $objAsHash{value} = 42; say "$objAsHash{value}"; say $$obj;

    Edit: BTW, the fact that $method = "method_name"; $obj->$method(); works even under strict might help you come up with another solution to print the output of method calls without concatenating bits of strings. Maybe something like: print replace $self, "Values: {self:get_value1}, {self:get_value2}, {self:get_value3}"; where replace uses s///ge;
    (so now that I think of it, the tied hash isn't the only solution I can come up with)

    Edit2: haukex is right. Using a templating engine is probably the best idea

Re: Method for reducing tedium of transferring object properties to scalars
by Your Mother (Archbishop) on Apr 21, 2017 at 18:29 UTC

    Seems you are in search of a view. Objects should generally not deal with STDOUT; code smell. This is just a terse example to demonstrate, not to recommend any specific packages.

    #!/usr/bin/env perl use strictures; use Template; package OHAI { use Moo; has [qw/ val1 val2 val3 /] => is => "ro"; }; my $tt2 = Template->new; my $hai = OHAI->new({ val1 => "I", val2 => "CAN HAZ", val3 => "C0DE" }); $tt2->process(\*DATA, { thing => $hai }); __DATA__ Values: [% thing.val1 %] [% thing.val2 %] [% thing.val3 %]?
Re: Method for reducing tedium of transferring object properties to scalars
by shmem (Chancellor) on Apr 21, 2017 at 22:00 UTC

    The easiest way is creating a subroutine which takes the object as first argument, then an array of methods, calls those methods and returns something convenient - either a list (key/value pairs, or just the values) or a reference thereof:

    sub get_props { my $obj = shift; # return just a list of values return ( map { $obj->$_ } @_ ); # return a list of key/value pairs return ( map { $_, $obj->$_ } @_ ); # to return references, just wrap the previous examples # into [] or {} as appropriate } my @meths = map { "get_value$_" } 1..3; # or whatever your methods are + really print "Values: @{[get_props($obj, @meths)]}\n"; # tweak to fit get_pro +ps()

    Of course, that only works for method calls without arguments. If your methods need arguments, you'd tweak that sub to take named arguments (i.e. it is passed a list of key/value pairs where the key is the method, and the value is a reference to whatever the method takes), or a hash reference holding that. Don't use that stub as is. There's no check for error conditions etc.

    update: It really depends on what your objects are, and what you are up to. If you are the Master of the Class/Package you are using, and if your objects are just scalar references (as any well behaved object should be), you could use overload to provide a shortcut:

    package Object; use Hash::Util::FieldHash; use overload '%{}' => \&_get_props, fallback => 1; Hash::Util::FieldHash::fieldhash my %objects; sub new { my $class = shift; my $self = bless do { \my($x) }, $class; $objects{$self} = { prop1 => 'foo', prop2 => 'bar', blorf => sub {time}, }; bless $self, $class; } # getters/setters... sub get_prop1 { $objects{shift(@_)}->{prop1}; } # ... and so on sub _get_props { my $obj = shift; $objects{$obj}; +{ map { $_, ref $objects{$obj}->{$_} eq 'CODE' ? $objects{$obj}->{$_}->() : $objects{$obj}->{$_} } keys %{$objects{$obj}} } } 1; __END__
    use Object; my $foo = Object->new; print "Values: ", join(", ", @$foo{qw(prop1 blorf)}),"\n"; __END__ Values: foo, 1492814598

    Ah well... TIMTOWTDI - there are so much ways to wrap laziness into magic... ;-)

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re: Method for reducing tedium of transferring object properties to scalars
by dsheroh (Monsignor) on Apr 22, 2017 at 08:40 UTC
    I notice that, in all your examples of what you want to do, the object you're working with is $self. If that's also true of your actual use case, then you can take advantage of the fact that your object is (almost certainly) implemented as a blessed hash reference and make use of a hash slice to extract values en masse from the underlying hash:
    #!/usr/bin/env perl use strict; use warnings; use 5.010; package Foo; sub new { return bless $_[1] } sub show_properties { my $self = shift; my ($foo, $bar, $baz) = @$self{qw( foo bar baz )}; say "foo: $foo, bar: $bar, baz: $baz"; } package main; my $foo = Foo->new( { foo => 1, bar => 2, baz => 3 } ); $foo->show_properties;
    Note that, because this accesses the underlying data without using any accessors, then you may get incorrect results if your accessors do anything more than just forward the underlying data to the caller (e.g., calculated properties).

    Generally speaking, you can also do this to suck property values out of other objects[1], but that's a blatant violation of encapsulation and ties you directly to the internal implementation of the other class, so it's probably not a particularly good idea. (Doing this with $self similarly ties you to the internal implementation as well, of course, but, since it's within the same class, I don't consider it that big a deal. Others disagree quite strongly, and will maintain that accessors should always be used, without exception, even within the same class.)

    [1] ...because almost all objects in Perl are blessed hashrefs and because "Perl doesn't have an infatuation with enforced privacy. It would prefer that you stayed out of its living room because you weren't invited, not because it has a shotgun."

Re: Method for reducing tedium of transferring object properties to scalars
by LanX (Saint) on Apr 22, 2017 at 01:18 UTC
    You are asking several different questions in the same time:
    • template like interpolation of method calls
    • efficient mapping of method calls to scalars
    • shortening method calls, not only for interpolation
    I'm confused. .. my best guess is that you want something like a with(object){ } statement like in JS...

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      my best guess is that you want something like a with(object){ } statement like in JS...

      Oh! great oppotunity for being silly...

      package Foo { sub new { bless { foo => 'bar', baz => 'quux', }; } sub get_foo { $_[0]->{foo}; } sub get_baz { $_[0]->{baz}; } } sub with (&$@) { my $code = shift; my $obj = shift; map { my $meth = $_; local $_ = $obj->$meth; $code->(); } @_; } my $obj = Foo->new; my @methods = qw(get_foo get_baz); with { print "val: $_\n"; } $obj, @methods;

      ;-)

      perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
        Roughly almost ... no ... almost roughly ...

        ;)

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!