Perl: Hash slices cannot be lexically scoped - perl

I have really no idea, why this is wrong:
#!/usr/bin/perl
use v5.20;
package MyDate;
sub new{ bless {}, shift; }
sub AUTOLOAD{
my $f = our $AUTOLOAD;
my #h{qw[Wday Month Year]} = (localtime)[3,4,5];
}
Err:compilation error near "#h{"
If I delete my (or even if package-scoped with our):
#h{qw[Wday Month Year]} = (localtime)[3,4,5];
It will magically works. Why cannot be hash slices lexically scoped?
Edit: Yes - I have not noticed, that (localtime)[3] = mday not wday. But that is not the point. I am asking about the scope, not localtime func.
Edit2: The hash %h (the point of my question), is intended to be used inside the autoload sub (well, of course when I am trying to use it as hash slice there). Just for clarity.

#h{...} is not a variable, so you can't declare it as such.
#h{...} = ...; sets elements of %h. So it's %h you need to create.
This is done as follows:
my %h;
By the way, I doubt you have a legitimate reason for using AUTOLOAD. Keep in mind that code at the top level (at the file level) of a module will be executed when the module is first loaded in an interpreter.

I hope that you will see your mistakes from following piece of code
use strict;
use warnings;
use diagnostics;
use v5.20;
package MyDate;
sub new{ bless {}, shift; }
sub AUTOLOAD{
my $f = our $AUTOLOAD;
#my %h; # !!! without hash declaration compilation error
# 0 1 2 3 4 5 6 7 8
#my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
# localtime(time);
#h{qw[Wday Month Year]} = (localtime)[6,4,5];
}
perl hash_package.pl
Global symbol "%h" requires explicit package name (did you forget to declare "my %h"?) at hash_package.pl line 15.
Execution of hash_package.pl aborted due to compilation errors (#1)
(F) You've said "use strict" or "use strict vars", which indicates
that all variables must either be lexically scoped (using "my" or "state"),
declared beforehand using "our", or explicitly qualified to say
which package the global variable is in (using "::").
Uncaught exception from user code:
Global symbol "%h" requires explicit package name (did you forget to declare "my %h"?) at hash_package.pl line 15.
Execution of hash_package.pl aborted due to compilation errors.

This is just a big WAG at what the OP is trying to do. So it's a pretty crappy SO answer according to site conventions, but I think it might help unmuddy the waters for our suffering OP.
I'm going to start with the code the OP posted, comment a bit on it, and then move to several examples of the "right way to do it".
OP's code:
#!/usr/bin/perl
So we're running a script.
use v5.20;
With version 5.20 or better. So far, so good.
package MyDate;
Now we've selected a new namespace/package called MyDate. While this isn't illegal, it is generally considered desirable to have one package per file.
sub new{ bless {}, shift; }
We have a constructor. So MyDate is going to be a class. Maybe worth looking at Moose or Moo for help with automating some of the boring crap with class construction. But there isn't anything wrong with using good, ole' classical Perl objects.
sub AUTOLOAD{
my $f = our $AUTOLOAD;
my #h{qw[Wday Month Year]} = (localtime)[3,4,5];
}
The syntax error, the source of all pain. AUTOLOAD is called to handle any unknown function calls in the namespace. So, this in-effect, intercepts all undefined method calls. MyDate has an infinite list of methods. It's probably not what is really needed.
Let's rework things a bit:
Here's my guess at the sort of thing the OP may want in their script file:
#!/usr/bin/perl
use v5.20;
use strict; # Make life easier by catching bugs at compile time.
use warnings; # Catch things that probably indicate errors, but aren't technically illegal.
use MyDate; # Load my date class
# Make some dates
my $today = MyDate->new();
my $aprilish = MyDate->new( month => 4 );
# Do stuff with them!
print_date( $today );
print_date( $aprilish );
sub print_date {
my ($date) = #_;
say "Weekday: ", $date->wday();
say "Month: ", $date->month();
say "Year: ", $date->year();
}
Library File: MyDate.pm
package MyDate;
use v5.20; # Set the minimum perl version required. Optional, but useful.
use strict; # Always
use warnings;
sub new {
my ($class, %parts) = #_;
my %defaults; #defaults[wday month year] = localtime(3, 4, 5)
my $self = bless {
%defaults,
}, $class;
for my $part ( qw/ month wday year /) {
next unless exists $parts{$part};
$self->$part( $parts{$part} ); # Call the associated method to initialize an attribute.
delete $parts{$part};
}
die "Unknown attributes: ", join ', ', keys %parts # Fatal error on unknown args
if %parts;
return $self;
}
# The other methods are mostly identical.
sub month {
my ($self, $value) = #_;
if ( #_ == 2 ) { # If two args are passed, we are a setter.
$self->{month} = $value;
}
return $self->{month};
}
That's a classical perl OO version of something like the OP is going for, I think.
It's a lot less hassle to write with Moo.
package MyDate;
use v5.20; # Set the minimum perl version required. Optional, but useful.
use Moo; # Turns on strict and warnings;
use namespace::autoclean; # Removes any utility functions so they don't show up as methods.
has 'month' => (
is => 'ro',
builder => 1,
);
has 'wday' => (
is => 'ro',
builder => 1,
);
has 'year' => (
is => 'ro',
builder => 1,
);
sub _build_month { localtime()[4] }
sub _build_wday { localtime()[3] }
sub _build_year { localtime()[5] }
But probably the best thing to do would be to take an existing date manipulation library like DateTime and use it.
#!/usr/bin/perl
use v5.20;
use strict; # Make life easier by catching bugs at compile time.
use warnings; # Catch things that probably indicate errors, but aren't technically illegal.
use DateTime; # Load my date class
# Make some dates
my $today = DateTime->today();
my $aprilish = DateTime->today()->set_month( 4 );
# Do stuff with them!
print_date( $today );
print_date( $aprilish );
sub print_date {
my ($date) = #_;
say "Weekday: ", $date->day_of_week();
say "Month: ", $date->month();
say "Year: ", $date->year();
}
Anyhow, I hope that all this is useful to the OP, and maybe, just maybe, to someone else.

Related

How to add a new syntax feature for perl?

I want to add a new feature for Perl language, in order to type less $self->.
For example, Translate:
use Moo;
has a_attr => (is=>'rw');
sub XXX {
print $self->a_attr;
}
To:
use Moo;
use MyFeatureModule;
has a_attr => (is=>'rw');
sub XXX {
print _a_attr;
}
How-to?
This doesn't require any changes to Perl's syntax, only to its semantics. Luckily, that's not too hard.
What you want can be achieved by providing an AUTOLOAD sub for your package, which will kick in automatically whenever you call a sub that hasn't been defined yet (i.e. _a_attr in your example). This AUTOLOAD method can then use Devel::Caller to grab $_[0] (i.e. $self) from its caller, inject it onto #_ and then goto the original method.
use v5.14;
use strictures;
package Foo {
use Moo;
has xyzzy => (is => 'ro', default => 42);
sub sayit {
say _xyzzy();
}
sub AUTOLOAD {
require Devel::Caller;
my ($invocant) = Devel::Caller::caller_args(1);
unshift #_, $invocant;
my ($method) = (our $AUTOLOAD =~ /::_(\w+)\z/)
or die "Method not found!";
my $coderef = $invocant->can($method)
or die "Method not found!";
goto $coderef;
};
}
my $obj = Foo->new;
$obj->sayit;
Is this a good idea? Well, I certainly wouldn't do it. As well as introducing an unnecessary level of slow-down to your code, and breaking inheritance, it is likely to confuse anybody who has to maintain your code after you. (And that might be your future self if you take a break from the project, and come back to it in 6 months.)

Can I define a subroutine that can be called like a built-in, everywhere?

Currently, during debugging, I tend to insert
carp Dumper \#foo, \%bar;
statements into my code, and regularly run into the problem that Carp and Data::Dumper are not imported in the current package. To avoid this problem, I defined the following sub in a central file that's always included:
sub main::bla {
use Carp; use Data::Dumper;
carp Dumper #_;
}
So now I can do
main::bla \#foo, \%bar;
everywhere, but the main:: annoys me. Is there something like a global package that's always imported, in any package, automatically (just like built-ins practically are imported everywhere)?
You could just call it
::bla( \#foo, \%bar );
In earlier days, I used to put util functions in a Ut package. And that was fine, but I noticed that my code wasn't really as modular as I thought of it. Each module that depended on Ut functions being there could only succeed if somebody took the trouble to build that namespace.
In the end, I considered use and require statements as simply documenting dependencies. There are more flexible ways to change what code is called by library modules, rather than changing their implementation in main.
For example, you could do this in a single use statement.
use Ut blah => sub {
use Carp;
use Data::Dumper;
carp Dumper #_;
};
And define the import:
sub import {
shift; # It's just me
my ( $name, $impl ) = #_;
if ( $name eq 'blah' && ref( $impl ) eq 'CODE' ) {
*blah = $_[1];
}
...
}
I still use the ut pattern, when I'm developing a lot of code at once. But I don't mind writing
ut:dump_var( $var )
as opposed to saving 3-4 characters (because sometimes I call it U::).
Now, it appears that you don't want to do this long term, and dumping out your variables is a useful thing for development. If you really want to do this, Smart::Comments does it like so:
### #foo
### %bar
All it takes is one use statement.
use Smart::Comments;
Maybe just better to make another Package with Export and needed things?
Like, MyTest.pm:
package MyTest;
use strict;
use Carp;
use Data::Dumper;
use base qw( Exporter );
our #EXPORT = qw(
debug
)
sub debug {
carp Dumper #_;
}
1;
So you can then just write in your script:
use MyTest;
debug {a => 'b', c => 'd' }
Fun fact: Some symbols are magic in that they always refer to their values in the main package. You can assign subroutines to these symbols that and they will be visible in any package.
{
package Foo;
# special names _ ARGV ARGVOUT ENV INC SIG STDERR STDIN STDOUT
sub ENV { print "In main::ENV ...\n" }
sub STDIN { print "In main::STDIN ...\n" }
sub _ { print "In main::_\n" }
# names that begin with ^ + upper case letter, or all digits
*{^T} = sub { scalar localtime };
*{^Gmtime} = sub { scalar gmtime };
*43 = sub { 42 };
use Data::Dumper;
*{^D} = \&Data::Dumper::Dumper;
}
{
package Bar;
&ENV;
STDIN();
print "The time is ", &^T, "\n";
print "In London it is ", &{^Gmtime}, "\n";
print "The answer is ", &43, "\n";
print "\#foo is ", &^D( \#foo );
}
None of this is recommended, unless you want to drive the next person who maintains your code insane.

Variable / Constant in UNIVERSAL?

I'm sure my problem is related to Namespaces and scoping, but I need some help!
I'm writing an OO Perl script with a fairly large number of classes and a requirement to minimise external module use (don't ask... I know, I know...)
So, I want to use UNIVERSAL to offer a logging method that every object can use.
Here's a very simple example that I've just whipped up.
use strict;
use warnings;
package House;
sub new {
my ( $class, %args ) = #_;
my $self = {
colour => $args{colour},
size => $args{size},
};
bless $self, $class;
return $self;
}
package Boat;
sub new {
my ( $class, %args ) = #_;
my $self = {
doors => $args{doors},
roof => $args{roof},
};
bless $self, $class;
return $self;
}
package main;
my $obj = Boat->new( colour => "red", size => "big" );
$obj->_logger("created a big red boat");
my $obj2 = House->new( doors => 1, roof => "yes" );
$obj2->_logger("created a house with a door and roof");
package UNIVERSAL;
use POSIX qw( strftime );
use Sys::Hostname;
my $error_log
= hostname() . "-" . strftime( "%Y-%m-%d_%H.%M", localtime ) . ".log";
sub _dump {
my ( $self, $data, $filepath ) = #_;
open my $fh, ">", $filepath or die "Cannot write to $filepath: $!";
print $fh $data;
}
sub _logger {
my ( $self, $data ) = #_;
my $timestamp = strftime( "%Y-%m-%d %H:%M:%S", localtime );
$self->_dump( $timestamp . " " . $data, $error_log );
}
__END__
The problem is the $error_log variable in the UNIVERSAL namespace doesn't seem to be accessible by the objects in other classes in the same way that the UNIVERSAL methods are.
Errors with my $error_log:
Use of uninitialized value $filepath in open at ./test_uni.pl line 47.
Use of uninitialized value $filepath in concatenation (.) or string at ./test_uni.pl line 47.
Cannot write to : No such file or directory at ./test_uni.pl line 47.
Actually, now I type this I wonder if a closure with a class method in UNIVERSAL would work.
While I go and try that, does anyone have any suggestions for me please?
Thanks!
==================== UPDATE ======================
A closure with a class method in UNIVERSAL seemed to work:
package UNIVERSAL;
use POSIX qw( strftime );
use Sys::Hostname;
{
sub ERROR_LOG {
return hostname() . "-" . strftime( "%Y-%m-%d_%H.%M", localtime ) . ".log";
}
}
And then I call it in UNIVERSAL::_logger with UNIVERSAL->ERROR_LOG.
BUT! I only want the ERROR_LOG filepath to be created once at runtime. With this it will evaluate it every time...
Is this the only way? How can I access variables in the UNIVERSAL package from elsewhere?
Thanks!
The problem in your case is just that you run the code before the stuff in UNIVERSAL is set up.
Move the main package all the way down, or wrap UNIVERSAL in a BEGIN block, and it works.
Nothing weird about UNIVERSAL or my going on here.
Update: Okay, it is a bit weird that you can call _logger (so that part is loaded already), but the $error_log is not there yet. Here is a minimal example that demonstrates this behaviour (remove the BEGIN block to see the problem):
use strict;
use warnings;
ABC->hey();
package ABC;
BEGIN{
my $x = 1;
sub hey(){
print "x = $x";
}
}
Maybe this explains it:
A my has both a compile-time and a run-time effect. At compile time, the compiler takes notice of it. The principal usefulness of this is to quiet use strict 'vars' , but it is also essential for generation of closures as detailed in perlref. Actual initialization is delayed until run time, though, so it gets executed at the appropriate time, such as each time through a loop, for example.
My reading would be that the variable declaration and the subroutines are compiled before the code is executed, but that the assignment of the value does not take place until the line which does it is reached again (which in your case is after you call the subroutines which are closures around the still uninitialized value).

How can I make Perl die when reading, but not writing, to non-existing keys in deep hash?

I'm using dynamic multilevel hashes from which I read data but also writes data.
A common pitfall for me is accessing non-existing keys (typos, db revisions etc.). I get undefs which propagate to other parts and cause problems. I would like to die whenever I try to read a non-existing key, but still be allowed to add new keys.
So the wanted behavior is:
my %hash;
$hash{A} = 5; # ok
print $hash{A}, "\n"; # ok
print $hash{X}, "\n"; # should die
$hash{B}{C}{D} = 10; # ok
print $hash{B}{C}{X}, "\n"; # should die
I previously posted a similar question and got great answers. I especially like the accepted one, which allows using the normal hash syntax. The only problem is I'm not sure how to easily generalize this to deep hashes as in the example above.
p.s.
I find this feature really useful and I wonder if I'm missing something, since it does not seem very popular. Perhaps it is not common to read/write from/to the same hash?
With warnings pragma switched on then you do get Use of uninitialized value in print at... warnings at the two lines you want to die.
So if you make warnings fatal then they would die instead:
use warnings FATAL => 'all';
Update
Based on comments you've made I assume your common case issue is something along these lines:
my $x = $hash{B}{C}{X};
Which won't throw warning/error until you actually use $x later on.
To get around this then you can do:
my $x = $hash{B}{C}{X} // 'some default value';
my $z = $hash{B}{C}{Z} // die "Invalid hash value";
Unfortunately the above would mean a lot of extra typing :(
Here is at least a short cut:
use 5.012;
use warnings FATAL => 'all';
use Carp 'croak';
# Value Or Croak!
sub voc { $_[0] // croak "Invalid hash" }
Then below would croak!
my $x = voc $hash{B}{C}{X};
Hopefully this and also the fatal warnings are helpful to you.
/I3az/
It's late for me so I'll be brief, but you could do this using the tie functionality -- have your hash represented by an object underneath, and implement the functions needed to interact with the hash.
Check out perldoc -f tie; there are also many classes on CPAN to look at, including Tie::Hash itself which is a good base class for tied hashes which you could build on, overriding a few methods to add your error checking.
If you want to wrap checks around a hash, create a subroutine to do it and use it as your interface:
use 5.010;
use Carp qw(croak);
sub read_from_hash {
my( $hash, #keys ) = #_;
return check_hash( $hash, #keys ) // croak ...;
}
But now you're starting to look like a class. When you need specialized behavior, start writing object-oriented classes. Do whatever you need to do. That's the part you're missing, I think.
The problem with sticking to the hash interface is that people expect the hash syntax to act as normal hashes. When you change that behavior, other people are going to have a tough time figuring out what's going on and why.
If you don't know what keys the hash might have, use one of the tied hash suggestions or just turn on warnings. Be aware that tying is very slow, nine times slower than a regular hash and three times slower than an object.
If you have a fixed set of possible keys, what you want is a restricted hash. A restricted hash will only allow you to access a given set of keys and will throw an error if you try to access anything else. It can also recurse. This is much faster than tying.
Otherwise, I would suggest turning your data into an object with methods rather than direct hash accesses. This is slower than a hash or restricted hash, but faster than a tied hash. There are many modules on CPAN to generate methods for you starting with Class::Accessor.
If your data is not fixed, you can write simple get() and set() methods like so:
package Safe::Hash;
use strict;
use warnings;
use Carp;
sub new {
my $class = shift;
my $self = shift || {};
return bless $self, $class;
}
sub get {
my($self, $key) = #_;
croak "$key has no value" unless exists $self->{$key};
return $self->{$key};
}
sub set {
my($self, $key, $value) = #_;
$self->{$key} = $value;
return;
}
You can get recursive behavior by storing objects in objects.
my $inner = Safe::Hash->new({ foo => 42 });
my $outer = Safe::Hash->new({ bar => 23 });
$outer->set( inner => $inner );
print $outer->get("inner")->get("foo");
Finally, since you mentioned db revisions, if your data is being read from a database then you will want to look into an object relation mapper (ORM) to generate classes and objects and SQL statements for you. DBIx::Class and Rose::DB::Object are two good examples.
Use DiveDie from Data::Diver:
use Data::Diver qw(DiveDie);
my $href = { a => { g => 4}, b => 2 };
print DiveDie($href, qw(a g)), "\n"; # prints "4"
print DiveDie($href, qw(c)), "\n"; # dies
re: your comment - hints on how to get the recursive effect on Ether's tie answer.
I'ts not for the fainthearted, but below is a basic example of one way that you might do what you're after by using Tie::Hash:
HashX.pm
package HashX;
use 5.012;
use warnings FATAL => 'all';
use Carp 'croak';
use Tie::Hash;
use base 'Tie::StdHash';
sub import {
no strict 'refs';
*{caller . '::hash'} = sub {
tie my %h, 'HashX', #_;
\%h;
}
}
sub TIEHASH {
my $class = shift;
croak "Please define a structure!" unless #_;
bless { #_ }, $class;
}
sub STORE {
my ($self, $key, $value) = #_;
croak "Invalid hash key used to store a value" unless exists $self->{$key};
$self->{$key} = $value;
}
sub FETCH {
my ($self, $key) = #_;
exists $self->{$key}
? $self->{$key}
: croak "Invalid hash key used to fetch a value";
}
1;
Above module is like a strict hash. You have to declare the hash structure up front then any FETCH or STORE will croak unless the hash key does exist.
The module has a simple hash function which is imported into calling program and is used to build the necessary tie for everything to work.
use 5.012;
use warnings;
use HashX;
# all my hashref are ties by using hash()
my $hash = hash(
a => hash(
b => hash(
c => undef,
),
),
);
$hash->{a}{b}{c} = 1; # ok
$hash->{a}{b}{c} = 2; # also ok!
$hash->{a}{b}{d} = 3; # throws error
my $x = $hash->{a}{b}{x}; # ditto
Remember this is a quick & dirty example and is untested beyond above. I'm hoping it will give you the idea of how it could be done using Tie::Hash and even whether it's worth attempting :)

Perl Class::Accessor failure, trivial example - why?

Can someone tell me why the main does not find the methods generated by Class::Accessor in this very small and trivial example ?
These few lines of code fail with
perl codesnippets/accessor.pl
Can't locate object method "color" via package "Critter" at
codesnippets/accessor.pl line 6.
see the code:
#!/opt/local/bin/perl
# The whole Class::Accessor thing does not work !!
my $a = Critter->new;
$a->color("blue");
$a->display;
exit 0;
package Critter;
use base qw(Class::Accessor );
Critter->mk_accessors ("color" );
sub display {
my $self = shift;
print "i am a $self->color " . ref($self) . ", whatever this word means\n";
}
Your code is out of order. If you want the color accessor to be available, you need to invoke mk_accessors before you create your object and start doing stuff with it. For example:
package Critter;
use base qw(Class::Accessor);
Critter->mk_accessors("color");
sub display {
my $self = shift;
print $self->color, ' ', ref($self), "\n";
}
package main;
my $c = Critter->new;
$c->color("blue");
$c->display;
More commonly, the Critter code would be in its own module (Critter.pm), and all of the mk_accessor magic would happen when your main script runs use Critter -- well before your script starts working with Critter and Varmint objects.
FM is giving you good advice. mk_accessors needs to run before the other code. Also, normally you'd put Critter in a separate file and use Critter to load the module.
This works because use has compile time effects. Doing use Critter; is the same as doing BEGIN { require Critter; Critter->import; } This guarantees that your module's initialization code will run before the rest of the code even compiles.
It is acceptable to put multiple packages in one file. Often, I will prototype related objects in one file, since it keeps everything handy while I am prototyping. It's also pretty easy to split the file up into separate bits when the time comes.
Because of this, I find that the best way to keep multiple packages in one file, and work with them as if I were using them, is to put the package definitions in BEGIN blocks that end in a true value. Using my approach, your example would be written:
#!/opt/local/bin/perl
my $a = Critter->new;
$a->color("blue");
$a->display;
BEGIN {
package Critter;
use base qw(Class::Accessor );
use strict;
use warnings;
Critter->mk_accessors ("color" );
sub display {
my $self = shift;
# Your print was incorrect - one way:
printf "i am a %s %s whatever this word means\n", $self->color, ref $self;
# another:
print "i am a ", $self->color, ref $self, "whatever this word means\n";
}
1;
}
I just wanted to provide you with a better solution -- feel free to downvote this to oblivion if the solution isn't welcome, but C::A is really a bad idea this day and age, use Moose:
package Critter;
use Moose;
has 'color' => ( isa => 'Str', is => 'rw' ); # Notice, this is typed
sub display {
my $self = shift;
printf (
"i am a %s %s whatever this word means\n"
, $self->color
, $self->meta->name
);
}
package main;
use strict;
use warnings;
my $c = Critter->new; # or my $c = Critter->new({ color => blue });
$c->color("blue");
$c->display;