in reply to Re: using Getopt::Long to modify the default values in new
in thread using Getopt::Long to modify the default values in new

instead of testing directly against the empty string, the code first changes an empty string into the string 'String', which is not only unnecessary, but in fact is the “common mistake” mentioned in the documentation.

Thanks for your generous comments, Athanasius. This is a good spot for me to repost. Even with edits, it goes on long enough for readmore tags. Output, source, and slightly different questions follow.

$ ./6.MY.translate.pl --configfile C --from tja --infile /home/bob/Doc +uments/meditations/Algorithm-Markov-Multiorder-Learner-master/data/2. +short.shelley.txt --outfile /home/bob/Desktop/1.state input is “The ancient teachers of this science,” said he, “promised impossibilities and performed nothing. The modern masters ... unfold to the world the deepest mysteries of creation. ---------------- begin block to change property is TO type is zilch expected type is zilch did execution get here? property is key type is zilch expected type is zilch did execution get here? property is FROM type is zilch expected type is zilch did execution get here? after block to change, self is bless({ format => 5.23, FROM => "en", key => 321, START => 1562878454, + TO => "ru" }, "My::Module") in new, param_hr is { CONTENT => "\x{201C}The ancient teachers of this science,\x{201D} sa +id he,\n\x{201C}promised impossibilities and performed nothing. The m +odern masters\npromise very little; they know that metals ... the steps\nalready marked, I will pioneer a new way, explore unknown p +owers, and\nunfold to the world the deepest mysteries of creation.\n\ +n", FROM => "tja", key => 123, TO => undef, } in new, self is bless({ format => 5.23, FROM => "en", key => 321, START => 1562878454, + TO => "ru" }, "My::Module") in sub key akey is 123 you called key() method on object 'My::Module=HASH(0x56161e3a1cb0)' key() : changing key to '123' in sub key Use of uninitialized value $akey in concatenation (.) or string at ./6 +.MY.translate.pl line 62. akey is you called key() method on object 'My::Module=HASH(0x56161e3a1cb0)' my key: 123 --- mod is bless({ format => 5.23, FROM => "en", key => 123, START => 1562878454, + TO => "ru" }, "My::Module") $

This is the source that produces that output:

#!/usr/bin/perl -w use 5.011; binmode STDOUT, ":utf8"; use open IN => ':crlf'; use open OUT => ':utf8'; package My::Module; sub new { my ( $class, $param_hr ) = @_; $param_hr = {} unless defined $param_hr; my %self = ( key => 321, format => 5.23, START => time(), 'FROM' => 'en', 'TO' => 'ru', ); say "begin block to change"; use Carp; my $self = \%self; for my $property ( keys %self ) { if ( exists $param_hr->{$property} ) { say "property is $property"; my $type = ref $param_hr->{$property} || 'zilch'; say "type is $type"; my $expected_type = ref $self{$property} || 'zilch'; say "expected type is $expected_type"; croak "$property should be a $expected_type" if $expected_type ne $type; say "did execution get here?"; #$self{$property} = delete $param_hr->{$property}; } } say "after block to change, self is"; bless $self, $class; dd $self; if ( exists $param_hr->{'key'} ) { say "in new, param_hr is"; use Data::Dump; dd $param_hr; say "in new, self is "; dd $self; } if ( exists $param_hr->{'key'} ) { $self->key( $param_hr->{'key'} ) +} else { warn "param 'key' is required."; return undef } return $self; # return hash, now blessed into a class instance, +hallelujah } # get or set the key sub key { say "in sub key"; my $self = $_[0]; my $akey = $_[1]; # optional key say "akey is $akey "; print "you called key() method on object '$self'\n"; if ( defined $akey ) { print "key() : changing key to '$akey'\n"; $self->{'key'} = $akey; } return $self->{'key'}; } 1; package main; use Getopt::Long; my $outfile = undef; my $configfile = undef; my $infile = undef; my $from = undef; my $to = undef; if ( !Getopt::Long::GetOptions( "outfile=s", \$outfile, "infile=s", \$infile, "configfile=s", \$configfile, "from=s", \$from, "to=s", \$to, "help", sub { print "Usage : $0 --configfile C [--outfile O] [--infile I] [--h +elp]\n"; exit 0; }, ) ) { die "error, commandline"; } die "configfile is needed (via --configfile)" unless defined $configfi +le; my $inFH; if ( defined($infile) ) { open( $inFH, '<:crlf:encoding(UTF-8)', $infile ) or die "opening input file $infile, $!"; } my $instr; { local $/ = undef; $instr = <$inFH> } close $inFH; if ( defined($instr) ) { say "input is $instr"; } say "----------------"; # uncomment only if My::Module is in separate file: #use My::Module; my $mod = My::Module->new( { 'key' => 123, 'CONTENT' => $instr, 'FROM' => $from, 'TO' => $to, } ); die unless defined $mod; print "my key: " . $mod->key() . "\n"; say "--- mod is"; dd $mod; __END__

How do the values in new and those from param_hr combine? I think it is supposed to happen in the line that I have commented out in order to get the script to run to termination:

#$self{$property} = delete $param_hr->{$property};

The way I read the delete function in perldoc.perl.org listing for delete, the RHS populates the left. If I uncomment that line with things the way they are, then I die more quickly than I want:

---------------- begin block to change property is TO type is zilch expected type is zilch did execution get here? property is key type is zilch expected type is zilch did execution get here? property is FROM type is zilch expected type is zilch did execution get here? after block to change, self is bless({ format => 5.23, FROM => "tja", key => 123, START => 1562881061 +, TO => undef }, "My::Module") param 'key' is required. at ./6.MY.translate.pl line 53. Died at ./6.MY.translate.pl line 133. $

I looked at several sources for references. While interesting, they stevieb's ref tutorial and perl 5.10 ref tut don't provide the explanation I'm looking for. Maybe it's not about ref as much as the ||.

I was thinking that the builtin type for ref was going to include String, but no:

SCALAR ARRAY HASH CODE REF GLOB LVALUE FORMAT IO VSTRING Regexp

So, how do I change this logic so that %self gets the key value pairs from $param_hr? Why does perl take the values from $mod and shunt them into $param_hr via @_ when new is called?

say "begin block to change"; use Carp; my $self = \%self; for my $property ( keys %self ) { if ( exists $param_hr->{$property} ) { say "property is $property"; my $type = ref $param_hr->{$property} || 'zilch'; say "type is $type"; my $expected_type = ref $self{$property} || 'zilch'; say "expected type is $expected_type"; croak "$property should be a $expected_type" if $expected_type ne $type; say "did execution get here?"; $self{$property} = delete $param_hr->{$property}; } } say "after block to change, self is";

Thanks for your comment

Replies are listed 'Best First'.
Re^3: using Getopt::Long to modify the default values in new
by bliako (Abbot) on Jul 12, 2019 at 10:27 UTC
    #$self{$property} = delete $param_hr->{$property};
    
    The way I read the delete function in perldoc.perl.org listing for delete, the RHS populates the left.
    

    From delete:

    In list context, usually returns the value or values deleted, or the last such element in scalar context. The return list's length corresponds to that of the argument list: deleting non-existent elements returns the undefined value in their corresponding positions.
    

    Perhaps it's not a good idea to delete the keys you set in $self from your input parameters. A simple $self{$property} = $param_hr->{$property}; suffices. You realise that by deleting, you modify a data structure created by the caller (%param_hr).

    The Use of uninitialized value $akey in concatenation (.) or string at ./6.MY.translate.pl line 62. can be avoided if you move say "akey is  $akey "; after you check it is defined

    Validating your input is great. But personally I would not validate the data types of params passed in a module's constructor. When you have a script and users run it from command line it is wise to (edit:over-)validate because a user may not have read the manual or is confused about input parameters. This is the normal scenario - judging from how I make such mistakes. However, I would categorise the programmer/user/caller of an API/module in slightly higher level and I would trust this user more. So I would still validate input params for right input values but checking for type, well that may cause me and the CPU too much extra work and just I do not do it. Practical reason: I sometimes set default params in $self to be undef. In which case no data type can be deduced. Saying in the pod that "this module does not validate input types, please observe the parameters' types stated" is enough for me. But may not be for others.

    Note that if ( exists $param_hr->{$property} ) { passes if key exists, but its value may be undefined. e.g. for this input: $param_hr{'key'} = undef; edit: clearly 'key' exists, so exists passes, but its value is undef which !perhaps! misses what author intended.