idioms for named parameters in object constructors in Perl - perl

In Perl, if I want to use named parameters in an object constructor, my code seems a bit clumsy if I wish to have some validation.
sub new {
my $class = shift;
my $self = {};
my %args = #_;
foreach my $argname (keys %args) {
if ($argname eq 'FOO') { $self->{$argname} = $args{$argname}; }
elsif ($argname eq 'BAR') { $self->{$argname} = $args{$argname}; }
elsif ($argname eq 'BAZ') { $self->{$argname} = $args{$argname}; }
…
else { die "illegal argument $argname\n"; }
}
bless $self;
return $self;
}
Firstly it seems a bit clumsy to have a temporary hash (%args). Secondly the whole if chain seems verbose and tedious.
The latter can be simplified to
if ('-FOO-BAR-BAZ-'=~m/-$argname-/) { $self->{$argname} = $args{$argname} }
else { die "..."; }
but I imagine this can be improved.
If I need to check values, the if … elsif chain is still necessary?
I've searched a little but cannot find a better idiom. Is there one (other than using a Perl OO framework of some sort)

I found myself constantly writing unnecessary code which checked the given parameters. But then I discovered Params::Validate. It is easy to use and if the validation fails it provides very clear and user-friendly error messages. Covering all possible combinations of parameters and their error messages is a tedious task. I prefer this way instead:
use Params::Validate qw/:all/;
sub new {
my $pkg = shift;
validate(
#_, {
foo => { type => SCALAR | ARRAYREF },
bar => { type => SCALAR, optional => 1},
baz => { type => ARRAYREF, default => ['value'] },
quux => { isa => 'CGI' }
}
);
return bless { #_ }, $pkg;
}
And later this code
MyApp::Something->new(
foo => 123,
bbr => 'typo',
quux => CGI->new()
);
becomes:
The following parameter was passed in the call to MyApp::Something::new but was not listed in the validation options: bbr
at test.pl line 14.
MyApp::Something::new(undef, 'foo', 123, 'bbr', 'typo', 'quux', 'CGI=HASH(0x7fd4fa1857e0)') called at test.pl line 27

You can use smart matching
my #validkeys = qw(FOO BAR BAZ);
if ($argname ~~ #validkeys) { # smart matching
$self->{$argname} = $args{$argname};
} else { die ... }
If you don't like the obscurity of the smart match operator you can swing together a regex
my $rx = '^' . join("|", #validkeys) . '$';
if ($argname =~ /$rx/) { ...

for validation you can define a hash of all legal argument and then just test, if the keys are in it or not
for example:
my %legal = ('FOO' => 1, 'BAR' => 1, 'BAZ' => 1);
my %args = #_;
foreach my $argname (keys %args) {
if(exists $legal{$argname}) { $self->{$argname} = $args{$argname}; }
else { die "illegal argument $argname\n"; }
}
about the clumsyness: well that's the to do it in perl
it can use hashes efficiently and the hash literals are readable

Warning! Untested code.
Check for valid keys.
die "invalid args" if grep { ! /^FOO|BAR|BAZ$/ } keys %args;
Store %args.
$self->{$_} = $args{$_} foreach(keys %args);

For completeness I'm adding this answer (to my own question) describing what I'm actually going to do, which is based on elements from several answers.
sub new {
my $package = shift;
# 1. validate argument names
my %args = #_;
my $valid = '^FOO|BAR|BAZ$';
for (keys %args) { die "invalid arg $_\n" unless /$valid/; }
# 2. construct instance from arguments
return bless { #_ };
}
I've accepted Sebastian's answer although I'm not using Params::Validate yet.
Notes:
I'm deploying to a server that has Perl 5.8 (really) but not Params::Validate. I have reasons for not yet pushing for the upgrades to 5.10.x etc.
For my specific circumstance the above strikes a good balance between brevity and readability. I can later add more validation without too much refactoring.
This compensates for one of the advantages of a getter/setter or accessor style methods for setting parameters (compiler catches typos in parameter name as that is the method name) whilst being more concise.
For other people the above will not apply, so I have accepted Sebastian's answer which I feel is the best one in general (YMMV).

Related

Automatically call hash values that are subroutine references

I have a hash with a few values that are not scalar data but rather anonymous subroutines that return scalar data. I want to make this completely transparent to the part of the code that looks up values in the hash, so that it doesn't have to be aware that some of the hash values may be anonymous subroutines that return scalar data rather than just plain old scalar data.
To that effect, is there any way to have the anonymous subroutines executed when their keys are accessed, without using any special syntax? Here's a simplified example that illustrates the goal and the problem:
#!/usr/bin/perl
my %hash = (
key1 => "value1",
key2 => sub {
return "value2"; # In the real code, this value can differ
},
);
foreach my $key (sort keys %hash) {
print $hash{$key} . "\n";
}
The output I would like is:
perl ./test.pl
value1
value2
Instead, this is what I get:
perl ./test.pl
value1
CODE(0x7fb30282cfe0)
As noted by Oleg, it's possible to do this using various more or less arcane tricks like tie, overloading or magic variables. However, this would be both needlessly complicated and pointlessly obfuscated. As cool as such tricks are, using them in real code would be a mistake at least 99% of the time.
In practice, the simplest and cleanest solution is probably to write a helper subroutine that takes a scalar and, if it's a code reference, executes it and returns the result:
sub evaluate {
my $val = shift;
return $val->() if ref($val) eq 'CODE';
return $val; # otherwise
}
and use it like this:
foreach my $key (sort keys %hash) {
print evaluate($hash{$key}) . "\n";
}
I don't believe that the words that others have written in disapproval of the tie mechanism are warranted. None of the authors seem to properly understand how it works and what core library backup is available
Here's a tie example based on Tie::StdHash
If you tie a hash to the Tie::StdHash class then it works exactly as a normal hash. That means there's nothing left to write except for methods that you may want to override
In this case I've overridden TIEHASH so that I could specify the initialisation list in the same statement as the tie command, and FETCH, which calls the superclass's FETCH and then makes a call to it if it happens to be a subroutine reference
Your tied hash will work as normal except for the change that you have asked for. I hope it is obvious that there is no longer a direct way to retrieve a subroutine reference if you have stored it as a hash value. Such a value will always be replaced by the result of calling it without any parameters
SpecialHash.pm
package SpecialHash;
use Tie::Hash;
use base 'Tie::StdHash';
sub TIEHASH {
my $class = shift;
bless { #_ }, $class;
}
sub FETCH {
my $self = shift;
my $val = $self->SUPER::FETCH(#_);
ref $val eq 'CODE' ? $val->() : $val;
}
1;
main.pl
use strict;
use warnings 'all';
use SpecialHash;
tie my %hash, SpecialHash => (
key1 => "value1",
key2 => sub {
return "value2"; # In the real code, this value can differ
},
);
print "$hash{$_}\n" for sort keys %hash;
output
value1
value2
Update
It sounds like your real situation is with an existing hash that looks something like this
my %hash = (
a => {
key_a1 => 'value_a1',
key_a2 => sub { 'value_a2' },
},
b => {
key_b1 => sub { 'value_b1' },
key_b2 => 'value_b2',
},
);
Using tie on already-populated variables isn't so neat as tying then at the point of declaration and then inserting the values as the data must be copied to the tied object. However the way I have written the TIEHASH method in the SpecialHash class makes this simple to do in the tie statement
If possible, it would be much better to tie each hash before you put data into it and add it to the primary hash
This program ties every value of %hash that happens to be a hash reference. The core of this is the statement
tie %$val, SpecialHash => ( %$val )
which functions identically to
tie my %hash, SpecialHash => ( ... )
in the previous code but dereferences $val to make the syntax valid, and also uses the current contents of the hash as the initialisation data for the tied hash. That is how the data gets copied
After that there is just a couple of nested loops that dump the whole of %hash to verify that the ties are working
use strict;
use warnings 'all';
use SpecialHash;
my %hash = (
a => {
key_a1 => 'value_a1',
key_a2 => sub { 'value_a2' },
},
b => {
key_b1 => sub { 'value_b1' },
key_b2 => 'value_b2',
},
);
# Tie all the secondary hashes that are hash references
#
for my $val ( values %hash ) {
tie %$val, SpecialHash => ( %$val ) if ref $val eq 'HASH';
}
# Dump all the elements of the second-level hashes
#
for my $k ( sort keys %hash ) {
my $v = $hash{$k};
next unless ref $v eq 'HASH';
print "$k =>\n";
for my $kk ( sort keys %$v ) {
my $vv = $v->{$kk};
print " $kk => $v->{$kk}\n"
}
}
output
a =>
key_a1 => value_a1
key_a2 => value_a2
b =>
key_b1 => value_b1
key_b2 => value_b2
There's a feature called "magic" that allows code to be called when variables are accessed.
Adding magic to a variable greatly slows down access to that variable, but some are more expensive than others.
There's no need to make access to every element of the hash magical, just some values.
tie is an more expensive form of magic, and it's not needed here.
As such, the most efficient solution is the following:
use Time::HiRes qw( time );
use Variable::Magic qw( cast wizard );
{
my $wiz = wizard(
data => sub { my $code = $_[1]; $code },
get => sub { ${ $_[0] } = $_[1]->(); },
);
sub make_evaluator { cast($_[0], $wiz, $_[1]) }
}
my %hash;
$hash{key1} = 'value1';
make_evaluator($hash{key2}, sub { 'value2#'.time });
print("$hash{$_}\n") for qw( key1 key2 key2 );
Output:
value1
value2#1462548850.76715
value2#1462548850.76721
Other examples:
my %hash; make_evaluator($hash{key}, sub { ... });
my $hash; make_evaluator($hash->{$key}, sub { ... });
my $x; make_evaluator($x, sub { ... });
make_evaluator(my $x, sub { ... });
make_evaluator(..., sub { ... });
make_evaluator(..., \&some_sub);
You can also "fix up" an existing hash. In your hash-of-hashes scenario,
my $hoh = {
{
key1 => 'value1',
key2 => sub { ... },
...
},
...
);
for my $h (values(%$hoh)) {
for my $v (values(%$h)) {
if (ref($v) eq 'CODE') {
make_evaluator($v, $v);
}
}
}
Yes you can. You can either tie hash to implementation that will resolve coderefs to their return values or you can use blessed scalars as values with overloaded mehods for stringification, numification and whatever else context you want to resolve automatically.
One of perl's special features for just such a use case is tie. This allows you to attach object oriented style methods, to a scalar or hash.
It should be used with caution, because it can mean that your code is doing really strange things, in unexpected ways.
But as an example:
#!/usr/bin/env perl
package RandomScalar;
my $random_range = 10;
sub TIESCALAR {
my ( $class, $range ) = #_;
my $value = 0;
bless \$value, $class;
}
sub FETCH {
my ($self) = #_;
return rand($random_range);
}
sub STORE {
my ( $self, $range ) = #_;
$random_range = $range;
}
package main;
use strict;
use warnings;
tie my $random_var, 'RandomScalar', 5;
for ( 1 .. 10 ) {
print $random_var, "\n";
}
$random_var = 100;
for ( 1 .. 10 ) {
print $random_var, "\n";
}
As you can see - this lets you take an 'ordinary' scalar, and do fruity things with it. You can use a very similar mechanism with a hash - an example might be to do database lookups.
However, you also need to be quite cautious - because you're creating action at a distance by doing so. Future maintenance programmers might well not expect your $random_var to actually change each time you run it, and a value assignment to not actually 'set'.
It can be really useful for e.g. testing though, which is why I give an example.
In your example - you could potentially 'tie' the hash:
#!/usr/bin/env perl
package MagicHash;
sub TIEHASH {
my ($class) = #_;
my $self = {};
return bless $self, $class;
}
sub FETCH {
my ( $self, $key ) = #_;
if ( ref( $self->{$key} ) eq 'CODE' ) {
return $self->{$key}->();
}
else {
return $self->{$key};
}
}
sub STORE {
my ( $self, $key, $value ) = #_;
$self->{$key} = $value;
}
sub CLEAR {
my ($self) = #_;
$self = {};
}
sub FIRSTKEY {
my ($self) = #_;
my $null = keys %$self; #reset iterator
return each %$self;
}
sub NEXTKEY {
my ($self) = #_;
return each %$self;
}
package main;
use strict;
use warnings;
use Data::Dumper;
tie my %magic_hash, 'MagicHash';
%magic_hash = (
key1 => 2,
key2 => sub { return "beefcake" },
);
$magic_hash{random} = sub { return rand 10 };
foreach my $key ( keys %magic_hash ) {
print "$key => $magic_hash{$key}\n";
}
foreach my $key ( keys %magic_hash ) {
print "$key => $magic_hash{$key}\n";
}
foreach my $key ( keys %magic_hash ) {
print "$key => $magic_hash{$key}\n";
}
This is slightly less evil, because future maintenance programmers can use your 'hash' normally. But dynamic eval can shoot the unwary in the foot, so still - caution is advised.
And alternative is to do it 'proper' object oriented - create a 'storage object' that's ... basically like the above - only it creates an object, rather than using tie. This should be much clearer for long term usage, because you won't get unexpected behaviour. (It's an object doing magic, which is normal, not a hash that 'works funny').
You need to identify when a code ref is present, then execute it as an actual call:
foreach my $key (sort keys %hash) {
if (ref $hash{$key} eq 'CODE'){
print $hash{$key}->() . "\n";
}
else {
print "$hash{$key}\n";
}
}
Note that you may consider making all of the hash values subs (a true dispatch table) instead of having some that return non-coderefs and some that return refs.
However, if you define the hash as such, you don't have to do any special trickery when it comes time to use the hash. It calls the sub and returns the value directly when the key is looked up.
key2 => sub {
return "value2";
}->(),
No, not without some ancillary code. You are asking for a simple scalar value and a code reference to behave in the same way. The code that would do that is far from simple and also injects complexity between your hash and its use. You might find the following approach simpler and cleaner.
You can make all values code references, making the hash a dispatch table, for uniform invocation
my %hash = (
key1 => sub { return "value1" },
key2 => sub {
# carry on some processing ...
return "value2"; # In the real code, this value can differ
},
);
print $hash{$_}->() . "\n" for sort keys %hash;
But of course there is a minimal overhead to this approach.

Perl - how to create function that accepts various data types

In some perl functions I want be able to work with various types of arguments and of course treat them differently. Now I am using for this ref function and execute appropriate part of code based on ref result, e.g.
sub method_that_accept_various_data_types(){
if (ref $_[0] eq "ARRAY"){
# ...
}
elsif (ref $_[0] eq "SCALAR"){
# ...
}
elsif (ref $_[0] eq "HASH"){
# ...
}
}
Is there some elegant way by which I can say that all my functions should follow this pattern or I have to use mentioned code at the beginning of all my subroutines? I am writing procedural code and I thing something similar is polymorphism and inheritance in OOP but I am newbie in this area.
Edit
All answers needs to append the code to all newly created function. I am asking if there is some generic way of doing this for every function defined from now without the need for adding code to new function.
You can also use hash of functions/dispatch table, but it is basically the same approach,
sub method_that_accept_various_data_types {
my $ref = ref($_[0]);
my $func = {
ARRAY => sub {
print "$ref\n";
},
SCALAR => sub {
print "$ref\n";
},
HASH => sub {
print "$ref\n";
},
}->{$ref} or return;
$func->(#_);
}
You could use the switch statement from the Switch module
use Switch;
sub polymorph_function {
my ($arg1, $arg2, ...) = #_;
switch (ref $arg1) {
case 'ARRAY' { ... }
case 'HASH' { ... }
...
}
}
I'm adding this based on Сухой27's example as an additional answer because I'd personally consider it bad code. But if you prefer short code you might like it:
sub polymorph_function {
({
ARRAY => sub { ... },
HASH => sub { ... },
...
}->{ref $_[0]} || return)->(#_);
}
It's basically the same, except that there are no temporary variables. It creates an anonymous hash and its reference is directly dereferenced and the appropriate key selected based on ref $_[0] (type of the 1st argument). The resulting coderef to one of the subs is then called immediately with all arguments to the original sub.
I just thought about ways to move much of the code to a central place.
sub polymorph_function {
poly \#_, (
ARRAY => sub { ... },
HASH => sub { ... },
...
);
}
sub poly {
my ($args, %subs) = #_;
my $sub = $subs{ref $args->[0]} or return;
$sub->( #$args );
}

How can I do function partial application in Perl?

Is there any way to achieve partial application in Perl?
Suppose, I want to do something like:
sub each_file($arr, $op) {
$op->($_) for #{$arr};
...
}
sub each_line($op, $file) {
...
}
each_file($arr, each_line($op));
I want to partially apply each_line() to only $op, so it'll become a new function can be passed to $each_file, how do I express this in idiomatic Perl?
You can do this in Perl with two approaches combined:
A function which returns a function reference
Closures
Example:
sub each_file {
my ($arr, $line_fn) = #_;
$line_fn->($_) for #{$arr};
...
}
sub each_line {
my ($op, $file) = #_;
...
}
sub make_line_processor {
my ( $op ) = #_;
# This is closed over $op, which effectively becomes
# a constant for the returned function
my $fn = sub {
return each_line( $op, #_ );
};
return $fn;
}
# To call it:
each_file( $arr, make_line_processor($op) );
This can be an even more useful technique in cases where you don't want $op directly, but some expensive-to-fetch derivation of it. In which case you would calculate the derived value just once (in the make_line_processor function) and close over that instead.
# given some $op as implied by your code snippet
each_file($arr, sub { each_line($op, shift) });
# shift op will be applied when anonymous sub { … } is called
(Your code snippet doesn't make it entirely clear what you intend $op to be when you make the call to each_line. It's usually better to present small working programs.)
You can roll this functionality up into a class. Then you can overload the subroutine dereference operator to make it look like your class is really a code reference.
package Partial;
use overload '&{}' => \&call;
sub new {
my $class = shift;
my $code = shift;
bless {code => $code, args => \#_}, $class;
}
sub call {
my ($self) = #_;
return sub{ $self->{code}->(#{$self->{args}}, #_) }
}
You can then use it like this:
sub printArgs {
print join ", ", #_;
print "\n";
}
my $partial = Partial->new(\&printArgs, 'foo', 'bar');
$partial->('baz', 'bat');
# prints foo, bar, baz, bat

Best way to use "isa" method?

What is the "best" way to use "isa()" reliably? In other words, so it works correctly on any value, not just an object.
By "best", I mean lack of un-handled corner cases as well as lack of potential performance issues, so this is not a subjective question.
This question mentions two approaches that seem reliable (please note that the old style UNIVERSAL::isa() should not be used, with reasons well documented in the answers to that Q):
eval { $x->isa("Class") }
#and check $# in case $x was not an object, in case $x was not an object
use Scalar::Util 'blessed';
blessed $x && $x ->isa($class);
The first one uses eval, the second uses B:: (at least for non-XS flavor of Scalar::Util).
The first does not seem to work correctly if $x is a scalar containing a class name, as illustrated below, so I'm leaning towards #2 (using blessed) unless somoene indicates a good reason not to.
$ perl5.8 -e '{use IO::Handle;$x="IO::Handle";
eval {$is = $x->isa("IO::Handle")}; print "$is:$#\n";}'
1:
Are there any objective reasons to pick one of these two approaches (or a 3rd one i'm not aware of) such as performance, not handling some special case, etc...?
The Scalar::Util implementation is categorically better. It avoids the overhead of the eval {} which always results in the setting of an additional variable.
perl -we'$#=q[foo]; eval {}; print $#'
The Scalar::Util implementation is easier to read (it doesn't die for a reason that is unknown to the code). If the eval fails too, I believe what happens is you have walk backwards in the tree to the state prior to the eval -- this is how resetting state is achieved. This comes with additional overhead on failure.
Benchmarks
Not an object at all
Rate eval su
eval 256410/s -- -88%
su 2222222/s 767% --
Object passing isa check
Rate su eval
su 1030928/s -- -16%
eval 1234568/s 20% --
Object failing isa check
Rate su eval
su 826446/s -- -9%
eval 909091/s 10% --
Test code:
use strict;
use warnings;
use Benchmark;
use Scalar::Util;
package Foo;
Benchmark::cmpthese(
1_000_000
, {
eval => sub{ eval{ $a->isa(__PACKAGE__) } }
, su => sub { Scalar::Util::blessed $a && $a->isa(__PACKAGE__) }
}
);
package Bar;
$a = bless {};
Benchmark::cmpthese(
1_000_000
, {
eval => sub{ eval{ $a->isa(__PACKAGE__)} }
, su => sub { Scalar::Util::blessed $a && $a->isa(__PACKAGE__) }
}
);
package Baz;
$a = bless {};
Benchmark::cmpthese(
1_000_000
, {
eval => sub{ eval{ $a->isa('duck')} }
, su => sub { Scalar::Util::blessed $a && $a->isa( 'duck' ) }
}
);
I used This is perl, v5.10.1 (*) built for i486-linux-gnu-thread-multi, and Scalar::Util, 1.21
You can wrap the safety checks in a scalar and then use the scalar as a method to keep things clean:
use Scalar::Util 'blessed';
my $isa = sub {blessed $_[0] and $_[0]->isa($_[1])};
my $obj;
if ($obj->$isa('object')) { ... } # returns false instead of throwing an error
$obj = {};
if ($obj->$isa('object')) { ... } # returns false as well
bless $obj => 'object';
if ($obj->$isa('object')) { say "we got an object" }
Note that $obj->$isa(...) is just a different spelling of $isa->($obj, ...) so no method call actually takes place (which is why it avoids throwing any errors).
And here is some code that will allow you to call isa on anything and then inspect the result (inspired by Axeman's answer):
{package ISA::Helper;
use Scalar::Util;
sub new {
my ($class, $obj, $type) = #_;
my $blessed = Scalar::Util::blessed $obj;
bless {
type => $type,
obj => $obj,
blessed => $blessed,
isa => $blessed && $obj->isa($type)
} => $class
}
sub blessed {$_[0]{blessed}}
sub type {$_[0]{isa}}
sub ref {ref $_[0]{obj}}
sub defined {defined $_[0]{obj}}
use overload fallback => 1,
bool => sub {$_[0]{isa}};
sub explain {
my $self = shift;
$self->type ? "object is a $$self{type}" :
$self->blessed ? "object is a $$self{blessed} not a $$self{type}" :
$self->ref ? "object is a reference, but is not blessed" :
$self->defined ? "object is defined, but not a reference"
: "object is not defined"
}
}
my $isa = sub {ISA::Helper->new(#_)};
By placing the code reference in a scalar, it can be called on anything without error:
my #items = (
undef,
5,
'five',
\'ref',
bless( {} => 'Other::Pkg'),
bless( {} => 'My::Obj'),
);
for (#items) {
if (my $ok = $_->$isa('My::Obj')) {
print 'ok: ', $ok->explain, "\n";
} else {
print 'error: ', $ok->explain, "\n";
}
}
print undef->$isa('anything?')->explain, "\n";
my $obj = bless {} => 'Obj';
print $obj->$isa('Obj'), "\n";
my $ref = {};
if (my $reason = $ref->$isa('Object')) {
say "all is well"
} else {
given ($reason) {
when (not $_->defined) {say "not defined"}
when (not $_->ref) {say "not a reference"}
when (not $_->blessed) {say "not a blessed reference"}
when (not $_->type) {say "not correct type"}
}
}
this prints:
error: object is not defined
error: object is defined, but not a reference
error: object is defined, but not a reference
error: object is a reference, but is not blessed
error: object is a Other::Pkg not a My::Obj
ok: object is a My::Obj
object is not defined
1
not a blessed reference
If anyone thinks this is actually useful, let me know, and I will put it up on CPAN.
Here's an update for 2020. Perl v5.32 has the isa operator, also known as the class infix operator. It handles the case where the left-hand argument is not an object it returns false instead of blowing up:
use v5.32;
if( $something isa 'Animal' ) { ... }
This might sound a little bit harsh to Perl, but neither one of these is ideal. Both cover up the fact that objects are a tack on to Perl. The blessed idiom is wordy and contains more than a couple simple pieces.
blessed( $object ) && object->isa( 'Class' )
I would prefer something more like this:
object_isa( $object, 'Class' )
There is no logical operation to get wrong, and most of the unfit uses will be weeded out by the compiler. (Quotes not closed, no comma, parens not closed, calling object_isa instead...)
It would take undefined scalars, simple scalars (unless they are a classname that is a Class), unblessed references, and blessed references that do not extend 'Class' and tell you that no, they are not Class objects. Unless we want to go the route of autobox-ing everything, we're going to need a function that tells us simply.
Perhaps there might be a third parameter for $how_close, but there could also be something like this:
if ( my $ranking = object_isa( $object, 'Class' )) {
...
}
else {
given ( $ranking ) {
when ( NOT_TYPE ) { ... }
when ( NOT_BLESSED ) { ... }
when ( NOT_REF ) { ... }
when ( NOT_DEFINED ) { ... }
}
}
About the only way I can see that we could return this many unique falses is if $ranking was blessed into a class that overloaded the boolean operator to return false unless the function returned the one value indicating an ISA relationship.
However, it could have a few members: EXACTLY, INHERITS, IMPLEMENTS, AGGREGATES or even MOCKS
I get tired of typing this too:
$object->can( 'DOES' ) && $object->DOES( 'role' )
because I try to implement the future-facing DOES in lesser perls (on the idea that people might frown on my polluting UNIVERSAL) on them.

Can I overload Perl's =? (And a problem while use Tie)

I choose to use tie and find this:
package Galaxy::IO::INI;
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my $self = {']' => []}; # ini section can never be ']'
tie %{$self},'INIHash';
return bless $self, $class;
}
package INIHash;
use Carp;
require Tie::Hash;
#INIHash::ISA = qw(Tie::StdHash);
sub STORE {
#$_[0]->{$_[1]} = $_[2];
push #{$_[0]->{']'}},$_[1] unless exists $_[0]->{$_[1]};
for (keys %{$_[2]}) {
next if $_ eq '=';
push #{$_[0]->{$_[1]}->{'='}},$_ unless exists $_[0]->{$_[1]}->{$_};
$_[0]->{$_[1]}->{$_}=$_[2]->{$_};
}
$_[0]->{$_[1]}->{'='};
}
if I remove the last "$[0]->{$[1]}->{'='};", it does not work correctly.
Why ?
I know a return value is required. But "$[0]->{$[1]};" cannot work correctly either, and $[0]->{$[1]}->{'='} is not the whole thing.
Old post:
I am write a package in Perl for parsing INI files.
Just something based on Config::Tiny.
I want to keep the order of sections & keys, so I use extra array to store the order.
But when I use " $Config->{newsection} = { this => 'that' }; # Add a section ", I need to overload '=' so that "newsection" and "this" can be pushed in the array.
Is this possible to make "$Config->{newsection} = { this => 'that' };" work without influence other parts ?
Part of the code is:
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my $self = {']' => []}; # ini section can never be ']'
return bless $self, $class;
}
sub read_string {
if ( /^\s*\[\s*(.+?)\s*\]\s*$/ ) {
$self->{$ns = $1} ||= {'=' => []}; # ini key can never be '='
push #{$$self{']'}},$ns;
next;
}
if ( /^\s*([^=]+?)\s*=\s*(.*?)\s*$/ ) {
push #{$$self{$ns}{'='}},$1 unless defined $$self{$ns}{$1};
$self->{$ns}->{$1} = $2;
next;
}
}
sub write_string {
my $self = shift;
my $contents = '';
foreach my $section (#{$$self{']'}}) {
}}
Special Symbols for Overload
lists the behaviour of Perl overloading for '='.
The value for "=" is a reference to a function with three arguments, i.e., it looks like the other values in use overload. However, it does not overload the Perl assignment operator. This would go against Camel hair.
So you will probably need to rethink your approach.
This is not exactly JUST operator overloading, but if you absolutely need this functionality, you can try a perl tie:
http://perldoc.perl.org/functions/tie.html
Do you know about Config::IniFiles? You might consider that before you go off and reinvent it. With some proper subclassing, you can add ordering to it.
Also, I think you have the wrong interface. You're exposing the internal structure of your object and modifying it through magical assignments. Using methods would make your life much easier.