How to check whether an attribute of an object has a value? - perl

I get an error such as "can't call method 'xxxx' on an undefined value" when attempting to check if an object has been created (by the perl module Bio::Perl).
Is there a general way of checking if an attribute has a value or not? I would have liked to do something like:
if ($the_object->the_attribute) {
But as long as the attribute is "undef", calling the method will only give me the error message. I have not been able to find a solution to this problem - which is real, because the object is created by the Bio::Perl module, and some attributes may or may not be set. Maybe I should add that I am not particularly perl-objects-savvy.
edit:
Below is a relevant part of my code. The get_sequence() function is in the Bio::Perl module. On line 13, how can I make sure that there is a value (sequence in this case) before checking the length of it?
my #msgs;
my #sequence_objects;
my $maxlength = 0;
for ( #contigs ) {
my $seq_obj;
try {
$seq_obj = get_sequence( 'genbank', $_ );
}
catch Bio::Root::Exception with {
push #msgs, "Nothing found for $_ ";
};
if ( $seq_obj ) {
my $seq_length = length( $seq_obj->seq );
if ( $seq_length > $maxlength ) {
$maxlength = $seq_length;
}
push #sequence_objects, $seq_obj;
}
}
...

if ($the_object->the_attribute) {
This checks if the return value of the method the_attribute is true. True means that it's not 0, the empty string q{} or undef.
But you said you want to know whether the object exists.
Let's go over some basics first.
# | this variable contains an object
# | this arrow -> tells Perl to call the method on the obj
# | | this is a method that is called on $the_object
# | | |
if ($the_object->the_attribute) {
# ( )
# the if checks the return value of the expression between those parenthesis
It looks like you're confusing a few things.
First, your $the_object is supposed to be an object. It probably came from a call like this:
my $the_object = Some::Class->new;
Or maybe it was returned from some other function call. Maybe some other object returned it.
my $the_object = $something_else->some_property_that_be_another_obj
Now the_attribute is a method (that's like a function) that returns a specific piece of data in your object. Depending on the implementation of the class (the building plan of the object), if that attribute is not set (initialized), it might either just return undef, or some other value.
But the error message you are seeing is not related to the_attribute. If it was, you'd just not call the code in the block. The if check would catch it, and decide to go to else, or do nothing if there is no else.
Your error message says you are trying to call a method on something that is undef. We know you are calling the the_attribute accessor method on $the_object. So $the_object is undef.
The easiest way to check if something has a true value is to just put it in an if. But you already seem to know that.
if ($obj) {
# there is some non-false value in $obj
}
You've now checked that $obj is something that is true. So it could be an object. So you could now call your method.
if ($obj && $obj->the_attribute) { ... }
This will check the true-ness of $obj and only continue if there is something in $obj. If not, it will never call the right hand side of the && and you will not get an error.
But if you want to know whether $obj is an object that has a method, you can use can. Remember that attributes are just accessor methods to values stored inside the object.
if ($obj->can('the_attribute')) {
# $obj has a method the_attribute
}
But that can blow up if $obj is not there.
If you're not sure that $obj is really an object, you can use the Safe::Isa module. It provides a method $_call_if_object1 that you can use to safely call your method on your maybe-object.
$maybe_an_object->$_call_if_object(method_name => #args);
Your call would translate to.
my $the_attribute = $obj->$_call_if_object('the_attribute');
if ($the_attribute) {
# there is a value in the_attribute
}
The same way you can use $_isa and $_can from Safe::Isa.
1) Yes, the method starts with a $, it's really a variable. If you want to learn more about how and why this works, watch the talk You did what? by mst.

Related

Complex data types, byReference arguments and data not changing as expected

I have a function that can either initialize itself and return an ordered dictionary with initial values, or if a collection is provided as an argument, it manipulates that collection. And that collection is a key within a parent collection.
In my actual code I am seeing an odd behavior, where a key in the initial collection is initially $Null or has a specified value, but when I try to revise that value I do NOT get an error, but I also do not get a changed value. However, when I try creating a minimally functional example to post here, it does work correctly, both in the console and the IDE.
So, given this code
function Write-Data {
param (
[System.Collections.Specialized.OrderedDictionary]$Collection
)
foreach ($key in $Collection.Keys) {
try {
$type = $Collection.$key.GetType().FullName
} catch {
$type = 'NULL'
}
Write-Host "$key $type $($Collection.$key)"
}
Write-Host
}
function Manage-Data {
param (
[System.Collections.Specialized.OrderedDictionary]$Collection
)
if (-not $Collection) {
[System.Collections.Specialized.OrderedDictionary]$initialCollection = [Ordered]#{
initialNull = $null
initialString = 'initial string'
}
return $initialCollection
} else {
$Collection.initialNull = 'No longer null'
$Collection.initialString = 'New String'
}
}
CLS
$parentContainer = New-Object System.Collections.Specialized.OrderedDictionary
$parentContainer.Add('data', (Manage-Data))
Write-Data $parentContainer.data
Manage-Data -Collection $parentContainer.data
Write-Data $parentContainer.data
is there any obvious scenario where either of the lines revising values would not throw an error, but would also not change the value? For example, if there are actually more functions doing other things with that initialized collection object before the attempt to revise data? Or perhaps more generally, since I am depending on the default byReference behavior of complex objects, is there some situation where this behavior breaks down and I am effectively modifying a new complex object when I think I am modifying the original? Or is the fact that I am having problems with a simple data type within the complex type potentially the issue?
For what it is worth, the idea here is to basically be able to use Dependency Injection, but with functions rather than classes, and also mimic to some extent the the concept of a constructor and a method in a class, but again in a function. And that has generally been working well, if a little messy, which has reenforced in my mind that I need to move to classes eventually. But this particular issue has me worried that I will see the same problem in classes, and unless I can understand it now I will have issues. But since I can't seem to recreate the issue in a simplified example, I seem to be unable to figure anything out.
It occurs to me that one thing I haven't tried is to actually get the memory address of the collection I think I am modifying, or even of the individual key, so I can verify I actually am changing the same data that I initialized. But HOW to get the memory address of a variable escapes me, and is maybe not possible in PowerShell or .NET?
"the default byReference behavior of complex objects" concerns the properties of the object not the object itself:
The difference between (if):
[System.Collections.Specialized.OrderedDictionary]$initialCollection = [Ordered]#{
initialNull = $null
initialString = 'initial string'
}
and (else)
$Collection.initialNull = 'No longer null'
$Collection.initialString = 'New String'
Is that the later (else) statements indeed change the values of the parent values as $Collection refers to the same object as $parentContainer.data but the former (if) statement creates a new $initialCollection in the scope of the Manage-Data function which isn't visible in the parent (even if you assign it to $Collection, it would create a new object reference in the scope of the Manage-Data function).
You might return $initialCollection but then, how are you handling the different returns (either a $initialCollection or enumerable null ) in your parent function? Therefore I would just return $initialCollection for both conditions and reassign the object (where the properties are still by reference and only the $parentContainer.data reference will change/reset):
$parentContainer.data = Manage-Data -Collection $parentContainer.data
Potential problems
In other words, the potential issue in your Manage-Data function lies in the fact that parent function needs a different approach in calling it based on the condition if (-not $Collection) which is actually defined within the function. (What will be the value of this condition, as the caller already need to act differently on the condition?)
This leaves two pitfalls:
You call the function in the assumption that the argument is not a collection but it actually is:
$parentContainer = [Ordered]#{ data = [Ordered]#{} }
$parentContainer.Add('data', (Manage-Data))
In this case you get an error:
MethodInvocationException: Exception calling "Add" with "2" argument(s): "Item has already been added. Key in dictionary: 'data' Key being added: 'data'"
And the opposite (which is less obvious): you call the function in the assumption that the argument is a collection but it is actually not:
$parentContainer = [Ordered]#{}
Manage-Data $ParentContainer.Data
This will leave an unexpected object on the pipeline:
(See: PowerShell Pipeline Pollution)
Name Value
---- -----
initialNull
initialString initial string
And doesn't add anything to the $parentContainer object:
$parentContainer # doesn't return anything as it doesn't contain anything
Suggestions
See about scopes
Enable Set-StrictMode -Version Latest.
This will show you that a property is potentially empty.
Use ([ref]$MyVar).Value = 'new value' to replace a value in a parent scope.
(not related to the question) Use the IDictionary interface: [Collections.IDictionary]$Collection to accept a more general collection type in your functions.

Creating a hash that is read-only outside of a module, but read/write inside

I am creating a module that has some fairly heavily nested hashes. The hash needs to be semi-regularly modified by the module, which unfortunately rules out using Map.
Generally, a branch of the nested hash will be returned to users of the module [1], and the simplest thing to do is to just return that nested hash, e.g.:
return %data{$branch}{$subbranch}
# ↪︎ %(subsubbranch1 => ... , subsubbranch2 => ... )
However, the nature of containers like arrays or hashes is that while you can make them read-only, the key/values can still be modified. The module users though should not actually modify those values for a number of reasons. Coercing to Map won't help, because if any of the values are also containers, they too will be modifiable.
My first thought was to subclass Hash (or otherwise make a custom Associative), but autovivification by default still goes to Hash. That, however, can be easily solved by overriding both AT-KEY and ASSIGN-KEY so that the AT-KEY returns an instance of the subclass if the key doesn't already exist:
class ProtectedHash is Hash {
has %!hash = ();
method EXISTS-KEY ($key) { %!hash{$key}:exists }
method ASSIGN-KEY ($key, \value) { %!hash{$key} = value }
method AT-KEY ($key) {
%!hash{$key} := ProtectedHash.new unless %!hash{$key}:exists;
%!hash{$key};
}
}
What I'd like to do is to fail if the ASSIGN-KEY (or the autovivification part of AT-KEY) is called from outside my module. I thought about using something like $?MODULE but that would be set at compile time and always be true. It looks like I can shimmy off of Backtrace a bit and check for the name of the file that called, but how consistent can I assume the call trace to those two functions?
For example, for ASSIGN-KEY I've got:
method ASSIGN-KEY ($key, \value) {
my #trace = Backtrace.new.list[3..*];
# The first three can be ignored:
# 0: code at ...Backtrace.pm6
# 1: method new at ...Backtrace.pm6
# 2: method AT-KEY at ...ThisFile.pm6
if/unless ??? {
%!hash{$key} = value
}
}
AT-KEY is normally called by the sub postcircumfix<{ }> (in which case #trace[0] can be ignored, and trace[1] would be the one of interest) but could also be, albeit rarely, called directly, in which case trace[0] is where I'd want to verify the file name.
Are there any other common ways in which AT-KEY or ASSIGN-KEY might be called? Or should check those two steps account for 99.9% of calls to those methods? [2]
[1] There are only a few subx4 branches that a user might want to manipulate, and so I figure it's best to provide them with the necessarily-slower .Hash method for when they really need it than to assume they always need a manipulable container. At times these may be called enough (particularly via a get-branch($foo){$subbranch}{$subsubbranch} pattern), that the addition overhead in creating a deepclone of the Hash becomes decently consequential.
[2] I'm not too concerned about preventing ANY access (although I'm certainly curious if that's possible purely via subclassing), because I'm sure that a fairly industrious coder could always figure something out, but I'd like to catch the most common ones as a way of saying "Can't touch this!" (cue the 90's music…) and provide an Awesome error message.
It's probably easier to achieve this by returning something wrapping the original Array or Hash, or alternatively using but to do a shallow copy and mix in to it (which means you retain the original type).
We can declare a role like this:
role Can'tTouchThis {
method AT-KEY(|) {
untouchable callsame
}
method ASSIGN-KEY(|) {
die "Cannot assign to this";
}
method AT-POS(|) {
untouchable callsame
}
method ASSIGN-POS(|) {
die "Cannot assign to this";
}
}
Where the sub untouchable is defined as:
multi untouchable(Positional \p) {
p but Can'tTouchThis
}
multi untouchable(Associative \a) {
a but Can'tTouchThis
}
multi untouchable(\o) {
o
}
Thus handling nested data structures by - on access - creating a read-only facade to those too.
Here's an example and some test cases to illustrate the effect:
class Example {
has %!foo = a => [ 1, 2, [ 3, 4] ], b => { c => { d => 42, e => 19 }, f => 100 };
method get($sym) {
untouchable %!foo{$sym}
}
}
given Example.new {
use Test;
# Positional cases
is .get('a')[0], 1;
is .get('a')[2][1], 4;
dies-ok { .get('a')[1] = 42 };
is .get('a')[1], 2;
# Associative cases
is .get('b')<c><d>, 42;
dies-ok { .get('b')<f> = 99 };
dies-ok { .get('b')<c><d> = 99 };
is .get('b')<f>, 100;
is .get('b')<c><d>, 42;
# Auto-viv also doesn't work
dies-ok { .get('a')[4]<a> = 99 };
dies-ok { .get('a')[4][0] = 99 };
}
Remove the untouchable call in the get method to see the majority of the tests here fail due to lack of protection.
The solution I ultimately employed served my needs, and I'm posting it here for those who may encounter similar situations. (The answer with role mixing unfortunately doesn't survive binding)
My ultimate approach was to worry the most about unintended editing. To protect against this, I created an Associative-type class called DB-Item that internally has a hash. The AT-KEY method returns the item from the hash if it exists, but ASSIGN-KEY and BIND-KEY simply immediately fail with an appropriate error message. The only other method is ADD-TO-DATABASE. That method handles adds leafs/branches depending on what it's passed (and in general end users should be wary of using all caps methods directly). Since branches can be of different lengths, this also greatly simplifies the initial DB creation:
class DB-Item does Associative {
has %!hash = ();
my $epitaph = "Modification of the database is not a good idea:\n" ~
" - Use .clone if you want to get a editable branch.\n" ~
" - If you really know what you're doing, use .ADD-TO-DATABASE";
method ADD-TO-DATABASE (*#branch) {
if #branch == 2 {
%!hash{#branch.head} = #branch.tail
}else{
%!hash{#branch.head} = DB-Item.new;
%!hash{#branch.head}.ADD-TO-DATABASE(#branch[1..*]);
}
}
method ASSIGN-KEY(|) is hidden-from-backtrace { die $epitaph }
method BIND-KEY(|) is hidden-from-backtrace { die $epitaph }
method EXISTS-KEY($key) { %!hash{$key}:exists }
method AT-KEY($key) { %!hash{$key}:exists ?? %!hash{$key} !! Nil }
method clone { ... }
}

Perl syntax (replacing 'defined(#array')

Let me start by saying I haven't programmed in Perl in a LONG time.
Currently trying to get some older code to work that relies on defined with an array.
Code (abridged):
# defined outside the file-reading block
my %refPRE;
# line from a file
#c = split();
if (defined #{$refPRE{$c[0]}})
{
# do stuff
}
Now this won't run like this because of the following error:
Can't use 'defined(#array)' (Maybe you should just omit the defined()?)
Fine, but if I removed the defined then I get the following error:
Can't use an undefined value as an ARRAY reference.
I can see what it's trying to do (if $c[0] is in the $refPRE then do this, else do something else) but I'm not familiar enough with Perl to figure out what the right way to get this to work is. Hoping this is trivial for someone.
Apparently posting here is all the catalyst I needed...
Switching if (defined #{$refPRE{$c[0]}}) to if ($refPRE{$c[0]}) was sufficient to work! Hopefully this helps someone else who searching for this (specific) problem...
Can't use an undefined value as an ARRAY reference.
This is saying that $refPRE{ $c[0] } is returning undef, and you cannot dereference undef as an array.
#{ undef } # will error
You don't need to deref this at all. If it returns undef, it's false. If it returns anything else, that will (likely) be true.
if ( $refPRE{$c[0]} )
{
my $foo = #{ $refPRE{$c[0]} };
# do stuff
}
Looking at your second error $refPRE{$c[0]} can be undefined so #{ ... } is failing. You can fix this using the undef or opperator // like this.
if (#{ $refPRE{$c[0]} // [] }) { ... }
This checks if $refPRE{$c[0]} is defined and if not returns an empty anonymous array. An empty array is false in an if statement.

Object class name in perl

I am kinda beginner in perl and I need know how can I check object class name.
My code is:
foreach my $y (keys %$x) {
print "$y\t$x->{$y}\n";
}
with output:
154176568 [object HTMLImageElement]
146292140 [object HTMLDocument]
153907016 [object HTMLImageElement]
I need to print just keys that are HTMLImageElement objects.
Now, question is:
(1) How can I check the class name
(2) How can I get/print class name
In Perl all classes magically extend the UNIVERSAL package. It has a method called isa() that you can use to do this:
foreach my $y (keys %$x) {
if( $x->{$y}->isa('HTMLImageElement') ) {
print "$y\t$x->{$y}\n";
}
}
Looking at the source for JE, it looks like JE::Object::Proxy is a subclass of JE::Object, and JE::Object has a stringification method (use overload fallback => 1, ... '""' => 'to_string' ...).
So when you do print "$y\t$x->{$y}\n";, this is printing the result of stringifying $x->{$y}.
You can stringify the object by putting it in double quotes, so "$x->{$y}". This expression will then have values such as you saw being printed, e.g. '[object HTMLImageElement]'.
If you want to pick up only HTMLImageElement objects, then you could check for these using an expression like
"$x->{$y}" eq '[object HTMLImageElement]'
If you especially want to extract the string 'HTMLImageElement' from the stringified value, you could do that using a regexp, e.g.
("$x->{$y}" =~ m{^\[object (.*)\]$}so)[0]
THOUGH, looking at the source for JE::Object::Proxy, JE::Object::Proxy has a method class which might perhaps return the name of the class that the object is a proxy for. So you might be able to get the class name using $x->{$y}->class, and then be able to test that directly as in $x->{$y}->class eq 'HTMLImageElement'.
If you want a string indicating the class name, use ref($object). This will return the reference type for a variable, which for perl objects, ends up being the package of the blessed object.
If you want to simply check if a variable is an instance of a certain class, use the isa() method. For instance:
if ($obj->isa('Animal::Dog')) {
push #dogs, $obj;
}

How can I check if an object has a specific method?

I want to use a method of an object.
Like $myObject->helloWorld().
However there are a couple of methods so I loop through an array of method names and call the method like this:
my $methodName ="helloWorld";
$myObject->$methodNames;
This works quite nice but some objects don't have all methods.
How can I tell whether $myObject has a method called helloWorld or not?
You can use the UNIVERSAL::can method of all objects to determine what methods it supports:
if ($myObject->can($methodName)) {
$myObject->$methodName;
}
As Eric noted, you can usually use UNIVERSAL::can
It can be used either on an object as in your example ($obj->can($methodName)) or statically, on a class: (CLASS->can($methodName))
Please note that there are possible false negatives associated with using UNIVERSAL::can on objects/classes which have AUTOLOAD-ed methods - see the perldoc for details. So before using can() on an object/class, please be careful to verify that the class in question either does not use AUTOLOAD, or overrides can() to compensate, or uses forward declaration to compensate as described in can()'s perldoc - hat tip to brian d foy)
Also, please be careful to either ONLY call can() on actual objects, or encapsulate it in eval. It will die if called on a non-object (e.g. undef, scalar etc...)
The canonical way to use can is inside an eval block in case the thing that you have in your scalar variable isn't actually an object. You don't have to worry about that because you'll still get the right answer (a non-object or class can't respond to the method):
if( my $ref = eval { $obj->can( $method ) } ) {
$obj->$ref( #args );
}
The can has the added feature that it returns a code reference to the method. Sometimes that can be handy.
I used this method when checking database connections, passed into a function, such as
my $method = "ping";
if(defined ($local_dbh) && eval{ $local_dbh->can($method) } ) {
if ($local_dbh->ping) {
return $local_dbh;
}
}
else {
## do connection
...
}