snakespan
snakespan

Reputation: 1211

Perl - Is there a way to import a class and all of its child classes?

In java, there is a way to import a class and all of its children in one line:

import java.utils.* 

In Perl, I've found I can only import specific classes:

use Perl::Utils::Folder;
use Perl::Utils::Classes qw(new run_class);

Is there similar way like java to import everything that falls under a tree structure, only in Perl?

Upvotes: 3

Views: 608

Answers (3)

Zac B
Zac B

Reputation: 4232

No, there is not a way to easily do what you are after.

You could walk the relevant paths in your PERL library's filesystem and use every .pm file you came across (that's what Module::Find, as suggested by @Daniel Böhmer, does), but that can miss a few things:

  • Packages that are declared in funny ways/at runtime.
  • Multiple packages per module file.
  • Other cases I haven't thought of.

This is also a bad idea, for a few reasons:

  • You mentioned "classes" in your question, rather than just packages. Perl packages and subpackages do not necessarily represent classes/instantiable object-oriented code. If you were to programmatically generate a list of all packages in a hierarchy and then call $packagename->new() on each of them, you might have a syntax error, if one of the packages was just a library of functions.
  • Packages and subpackages often are not directly related, developed by the same people, or used for similar things. Just because a package starts with Net:: doesn't mean that it will obey standard conventions that other Net::-prefixed packages expect. For example, File::Find and File::Tail share a prefix, but have very little to do with each other; the prefix is in common because both utilities work with files as their goal.
  • Lots of packages do things at BEGIN/INIT/etc time when they're compiled. Some of them (sadly) do different things depending on the order in which they're used relative to other modules. The solution to this problem for module developers is "don't do that", but for module users, it's "use sparingly, and only when needed".
  • It clutters your local namespace with lots of potentially-exported symbols you don't necessarily need (to conditionally import symbols, you'll have to use import arguments like you're doing in your example; there's no programmatic way to define "symbols I'm interested in", since Perl doesn't have that kind static analysis at compile time . . . not for lots of call styles, at least).
  • It slows down your program's startup time by compiling things you might not necessarily need. This might not seem important at the early phase of a project, but for larger projects it is very easy to end up in situations where you're pulling in over a thousand CPAN modules when you start Apache (or launch your main script, or whatever), and your app takes more than a minute just to start.

I have a hunch that you're trying to reduce boilerplate (as in: all of your modules have a big block of use statements at the top, and that's duplicated everywhere). There are a few ways to do this, starting with:

  • Don't: import things in each module as you need them, and use strict/warnings and lots of tests to be told early on if you're calling functionality that you haven't imported yet.
  • You could also make your own Exporter subclass that uses all of your standard modules and adds the functions that you frequently use from them to its @EXPORTS (or splices their @EXPORTS onto its own, or uses Exporter sub-sub-classing, or something).
  • Factor your code so that the parts that depend on multiple imported modules live in a single utility module, and import that.
  • Factor your code so that the parts that depend on the imported modules live in a parent class, and address its methods via instances of subclasses (or SUPER), so your subclasses don't have to explicitly contain the imports, e.g. $instance->method_that_calls_an_imported_function_in_the_parent();

Also, as an aside, using package.* imports in Java is debatable, and has many of the same drawbacks of doing it in Perl.

Upvotes: 5

David W.
David W.

Reputation: 107080

In Perl, the class Foo::Bar::Foo may not be a subclass of Foo::Bar. Nor, is there any guarantee that a subclass module even has the same class prefix. IO::File is a subclass of IO::Handle and not of IO which isn't even a module.

There also isn't even an easy way to tell of a Perl module is a sub-class of another Perl module. There are (at least) three ways to declare a subclass' relationship to a class:

  • use parent
  • use base
  • The @ISA package variable

It is possible to use @INC to find all modules, then look at the source and look at use parent, use base, and @ISA declarations and build a Perl class matrix, then go through that matrix to load the classes you do need. That will probably be slow and cumbersome, and doesn't even cover Moose based classes.

You're asking the wrong question. You're asking "Find all of the subclasses of a particular class.". This will include classes that you're probably not even interested in. I know (for example LWP) that there can be dozens of various classes and subclasses that include stuff you're not even interested in.

What you should be asking is "What do I need to do?", and then find the classes that fulfill your needs. If these classes happen to be child classes of a particular parent class, these subclasses will load the required class.

We do Java programming here, and one of the standards is not to use asterisks in our import statements. This is considered sloppy programming. If you need a particular class, you should declare it rather than simply declaring a superclass. Many of our reporting tools have problems with asterisk declarations in import statements.

There is a Module::Find module, but I am not sure exactly how it works. I believe it simply assumes that subclasses are in the same module hierarchy as the superclass, but that's far from true in Perl.

Upvotes: 5

vanHoesel
vanHoesel

Reputation: 954

In general, I think it is a bad idea to load a whole 'tree' of modules (or subclasses so to speak).

There is definitely something wrong in your design if you need to know all and everything about sub classes/modules. You break the rules of encapsulation and you should not need to know how a class handles its responsibilities.

Upvotes: 2

Related Questions