The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Package::Transporter::Generator::Constant_Function - base class for generating constant functions at compile time

SYNOPSIS

The following example demonstrates the creation and use of the constant functions ATB_NAME, ..., ATB_PRICE.

        package CF3;
        use strict;

        use Package::Transporter sub{eval shift}, sub {
                $_[0]->register_drain('::Enumerated', 'FOR_SELF', 'ATB_',
                        qw(NAME  SALE  STOCK  PRICE));
        };
        
        #...
        sub sell {
                my ($self, $amount) = @_;

        #       return if (ABT_SALE == IS_FALSE);
                $self->[ATB_STOCK] -= $amount;
                my $costs = $amount * $self->[ATB_PRICH];

                return($costs);
        }

Perl will tell you at compile time about the typing mistake ATB_PRICH, which could be seen as the main motivation for the exercise. The generated symbols can be inherited by other packages, therefore completing the OO system of Perl. The symbol IS_FALSE is such an example, it comes from a imaginary upstream package.

DESCRIPTION

The objective of this class is to promote a greater expressiveness of Perl5 code, while keeping fundamental programming practices intact. Practically this is achieved through constant functions, which are managed in a convenient manner. For details on constant functions, see the manual page perlsub. In short, such functions result in an inlined value, meaning that no actual subroutine call is performed by perl.

The class forms the base for ::Enumerated_CF, ::Random_CF, ::Lowered_CF, and ::Flatened_CF.

Motivation

Hashes are a popular approach to store information in Perl. Their keys are based on scalar strings, which opens the door for typing mistakes. Perl can't tell you about the typing mistake IS_OM_SALE in package CF0 (see below) at compile time. With Hash::Util you can set a trigger for a fatal exception once the erroneous code is executed, but that is unpredictable - maybe it never happens - and really late compared to the trivial nature of the error.

        # -- common approach using strings (without Package::Transporter)
        package CF0;
        use strict;

        my $states = { 'IS_ON_SALE' => 1 };

        my $apples = $states->{'IS_OM_SALE'};
        my $oranges = 1; # two times faster than apples

The example package CF1 (see below) demonstrates the creation and use of the constant function IS_ON_SALE. Oh, again a typing mistake. Good that IS_ON_SALE is an identifier and not a string, so Perl will tell you at compile time about the unknown identifier IS_OM_SALE. That is favorable over the case where the customer reports the error to you after the whole show crashed.

        # -- manually crafted constant function (without Package::Transporter)
        package CF1;
        use strict;

        sub IS_ON_SALE() { 1 };

        my $apples = IS_OM_SALE;
        my $oranges = 1; # same execution speed as apples

After the assignments to $apples and $oranges, both variables hold the same value "1". Because of internal optimizations in perl, there is even no impact on execution speed by using IS_ON_SALE. However, the expressiveness is higher. From reading the source it is clear that $apples are on sale and there is an amount of one orange left (in the basket). The technique to use symbols instead of actual values is even older than Perl, but not commonly found in Perl.

        package CF2;
        use strict;

        use Package::Transporter sub{eval shift}, sub {
                $_[0]->register_drain('::Flatened', 'FOR_SELF', 'IS_',
                        'ON_SALE' => 1);
        };

        my $apples = IS_ON_SALE;
        my $oranges = 1;

The example package CF2 (see above) demonstrates how to create the constant function with Package::Transporter. Regarding the amount of code required, the use of Package::Transporter is not efficient for trivial cases. Remember that the examples CF0, CF1, and CF2 are about the general motivation for constant functions, not about the motivation to use Package::Transporter.

Now that you are hopefully convinced about constant functions, you want to use them all over the place. How about random hash keys for your objects? Annoy all the people who mess around with the internals of your objects:

        package CF4;
        use strict;

        use Package::Transporter sub{eval shift}, sub {
                $_[0]->register_drain('::Random', 'FOR_SELF', 'ATB_',
                        qw(NAME  TYPE  STOCK  PRICE));
        };

        #...
        sub sell {
                my ($self, $amount) = @_;

                $self->{+ATB_STOCK} -= $amount;
                my $costs = $amount * $self->{+ATB_PRICE};

                return($costs);
        }

Inconsistencies in 'use strict' enforce a unary plus in front of the subroutine identifiers ATB_STOCK and ATB_PRICE. Without the plus, the identifiers would surprisingly be taken as unquoted strings. It is surprising, because it is the opposite of what 'use strict' was meant for. Fixed in Perl6.

To end this section and to do what its title implies, namely to motivate you, it should be mentioned that symbolic array indices can be shared between packages in a way that is called inheritance in the OO world.

The Prefix

This package can easily lead to many symbols being used, thus potentially increasing the chance of name clashes. Meaning that a name is allocated for conflicting purposes. You can protect from such clashes by using a meaningful prefix. Example: IMAP_CST_ as prefix for DISCONNECTED, to indicate that the symbol IMAP_CST_DISCONNECTED is about a disconnected IMAP client state.

PUBLIC INTERFACE REFERENCE

Only the public interface is documented in the following.

new

The constructor. Calling parameters: package visiting point (subroutine reference). Return value: a Package::Transporter::Package object.

drop

The sweeper. Removes the object from the Package::Transporter class with the intention of releasing the allocated memory. No calling parameter or return value.

KNOWN BUGS AND LIMITATIONS

WISHLIST

The author hopes that one day there will be a 'use stricter'. Which does, among other things:

  • Make its effect irreversible. Meaning there is no 'no stricter'.

  • Introduce 2 as irreversible flag in Internals::SvREADONLY(..., 2); with the purpose of locking the readonly flag.

  • Let Internals::SvREADONLY also prohibt re-blessing a locked reference, re-declaring a package namespace and re-defining a subroutine.

  • Complain about the unqouted string in { key => 'value' }. Yes, there is an unqouted string and it should be treated as such. Or at least give precedence to constant functions.

  • Make the namespace of a package read-only for other packages. (Indirect access to the symbol table via '::' is read-only.).

  • Disable direct access to the symbol table. No \*, no \%::. Or at least restricted to the current package.

  • Let 'package' return a reference to or name of the namespace.

AUTHOR

Winfried Trumper <pub+perl(a)wt.tuxomania.net>

COPYRIGHT

Copyright (C) 2009, 2010 Winfried Trumper

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

SEE ALSO

The manual page for Package::Transporter::Package contains the details of using and extending Package::Transporter.

Different approaches are provided by the Perl modules Exporter and constant.

The following packages provide support for objects as array references and are therefore loosely related to Transporter: Class-Gomor (there are more, but I can't find them right now).

The following packages treat arrays as objects, which Transporter is not about, but that also clarifies things: List::oo, Class-Builtin, Devel-Ladybug, and Object-Array.

The package POE is features a lot of constant functions

1 POD Error

The following errors were encountered while parsing the POD:

Around line 171:

You forgot a '=back' before '=head1'