Snow
Snow

Reputation: 447

In Perl, can I use constants to facilitate compile time detection of typos in method invocations?

I have the following program in Perl:

use Class::Struct;

use constant {
    ACTION => "Action",
    TYPE => "Type"
};

struct MyClass => {           
    &ACTION => '$',
    &TYPE => '$'
};

my $obj = new MyObj{
    &ACTION => 'add',
    &TYPE => '1'
}

# compilation error:
var $action = $obj->&ACTION;

I get a compilation error when trying to access the object field using a constant. Is it possible? What am I doing wrong?

Upvotes: 2

Views: 84

Answers (1)

Sinan Ünür
Sinan Ünür

Reputation: 118148

Your code includes some errors such as trying to instantiate MyObj when all you declared was MyClass and using var instead of my. With those fixed, you'll note that ->& is a syntax error. See Method Invocation and Method Call Variations.

You can use

  • Method Names as Strings
  • Subroutine References as Methods
  • Deferencing Method Call

That said, here is your code with the corrections to enable it to compile and run:

use strict;
use warnings;

use Class::Struct;

use constant {
    ACTION => "Action",
    TYPE => "Type"
};

struct MyClass => {
    ACTION() => '$',
    TYPE() => '$'
};

my $obj = MyClass->new(
    ACTION() => 'add',
    TYPE() => '1'
);

print $obj->${ \ACTION };

Note that using ACTION() rather than &ACTION in the declarations will turn the keys to compile time constants rather than runtime subroutine invocations (prefixing ACTION with & tells perl to ignore the empty prototype, effectively undoing the benefit of use constant).

It uses the Dereferencing Method Call:

Deferencing Method Call

Perl also lets you use a dereferenced scalar reference in a method call. That's a mouthful, so let's look at some code:

  $file->${ \'save' };
  $file->${ returns_scalar_ref() };
  $file->${ \( returns_scalar() ) };
  $file->${ returns_ref_to_sub_ref() };

The code above compiles to $obj->${ \'Action' }.

As usual, @ikegami spotted something I missed: $x->doesnotexist is a run-time error. By using constants as method names, it would be possible to detect typos at compile time. For example, if I had made a mistake such as $obj->${ \ACITON };, the compilation would have failed.

In that case, Const::Fast might provide an aesthetically more pleasing solution (even though it would come at a speed penalty relative to using constant subroutines):

use strict;
use warnings;

use Class::Struct;
use Const::Fast;

const my $ACTION => 'Action';
const my $TYPE => 'Type';

struct MyClass => {
    $ACTION => '$',
    $TYPE => '$'
};

my $obj = MyClass->new(
    $ACTION => 'add',
    $TYPE => '1'
);

print $obj->$ACION;

Output:

C:\...\Temp> perl -c tt.pl
Global symbol "$ACION" requires explicit package name (did you forget to declare "my $ACION"?) at tt.pl line 21.
tt.pl had compilation errors.

That is, such a typo would also be detected at compile time. There is a performance penalty for this over using constant subroutines, but it is less ugly.

Upvotes: 6

Related Questions