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

Hello Aldebaran,

I thought that I would emulate the syntax in source listing for Translate.pm. (Is this well-written?)

Not entirely — see below.

With this syntax, I believe that $param_hr is to be understood as an existing reference to a hash of parameters. I believe that this is the main data structure for this object.

Correct in both cases.

I've been looking at the rest of the code for new here. What does this do?

The for loop iterates through the keys of %self (in no particular order) — i.e., "key", "format", "model", etc. — assigning each in turn to $property. The if condition checks whether the current property is an entry in the user-supplied hash referenced by $param_hr:

But first, there is a check to ensure that the data type of the user-supplied entry matches the type expected by new(). This check is performed using the built-in function ref, which is documented as follows:

ref EXPR
...
Examines the value of EXPR, expecting it to be a reference, and returns a string giving information about the reference and the type of referent....

If the operand is not a reference, then the empty string will be returned. An empty string will only be returned in this situation. ref is often useful to just test whether a value is a reference, which can be done by comparing the result to the empty string. It is a common mistake to use the result of ref directly as a truth value: this goes wrong because 0 (which is false) can be returned for a reference.

— which is why I said above that the code is not entirely well-written: 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.

It would seem to check whether certain values are strings, but I don't understand the right hand side with
ref something || something else

No, it’s not checking whether values are strings, it’s checking whether values are references. (In this case, only the value associated with the headers key will actually be a reference.) The || (logical OR) operator checks its LHS first and returns it if it is true; otherwise, it returns the RHS. So in the lines:

my $type = ref $param_hr->{$property} || 'String'; my $expected_type = ref $self{$property} || 'String';

the first line assigns 'String' to $type if, and only if, the expression ref $param_hr->{$property} evaluates to a value which Perl considers “false.” If $param_hr->{$property} is not a reference, ref returns the empty string (""), which Perl does consider false, so the || operator evaluates to 'String'.

Hope that helps,

Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Replies are listed 'Best First'.
Re^2: using Getopt::Long to modify the default values in new
by Aldebaran (Curate) on Jul 11, 2019 at 22:44 UTC
    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.

      #$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.