Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Re: Defining classes and inheritance using packages within a single .pl file, without creating modules

by jbert (Priest)
on Nov 29, 2006 at 10:18 UTC ( [id://586649]=note: print w/replies, xml ) Need Help??


in reply to Defining classes and inheritance using packages within a single .pl file, without creating modules

Interesting.

The root cause is that perl has read your entire script from start to finish and compiled it, so that Class2::new exists as a method for you to call.

What hasn't happened at the time you are trying to execute $max->printHello is that the line @Class2::ISA = qw(Class1) hasn't been executed yet...simply because it is further down the script.

Normally, you pull in an external module as a seperate file with a use directive. This is like a require with a BEGIN block around it, which runs all the code in the module as well as compiling it.

You could fix this issue by either moving the { package ... } sections before your code or putting a BEGIN { ... } block around your package definitions.

However, I'd personally do neither of those. The best way these days to declare inheritance is use base, which also has the handly property of being a compile-time construct.

Also - to address your other issues, perl OO gets quite a bit nicer if you start to use some of the CPAN modules. The problem is working out which ones to use :-).

Class::Accessor is quite good, and Moose looks like it might be the way of the future. There is a discussion about this here from April of this year.

  • Comment on Re: Defining classes and inheritance using packages within a single .pl file, without creating modules
  • Select or Download Code

Replies are listed 'Best First'.
Re^2: Defining classes and inheritance using packages within a single .pl file, without creating modules
by bart (Canon) on Nov 29, 2006 at 11:33 UTC
    In general, I agree with your excellent advice, jbert, except for this minor point:
    However, I'd personally do neither of those. The best way these days to declare inheritance is use base, which also has the handly property of being a compile-time construct.
    IMO base is a crap module, it tries to handle both loading external files and inline packages, but it tries Too Much Magic™ and in some cases it may fail. It's failed many times for me in the past. I don't trust it any more.

    As a result, you may have to reorder the definition for your packages and possible even add a $VERSION variable (in an inline package!). All that to please base. Yuck.

    I wish we could bypass the magic, and explicitely tell it to load a module, or just skip that phase.

    If it wasn't for that annoyance, base would indeed work very well, for the reasons you give.

      That's interesting - and serves as an additional data point that consensus on current perl OO "best practice" is a bit limited at the moment. Yes, TIMTOWDI, but that can be confusing for newcomers.

      Do you think that these tutorials are still current? (Actually, I should have linked to these at first, sorry about that).

      By the way, would you care to elaborate on base's failure modes? Since you've caused me to look more closely, I can see how it might hurt if you're defining a module inline with the same name as one available as a seperate file via require - is that the main issue?

        I'm not sure how base has evolved over the years, but I've had problems where it died complaining it couldn't locate a module where the package was inline in the script further down, and it may indeed load an unwanted module of the same name (B is a favourite example) under similar circumstances. Fun for the whole family.

        Checking $VERSION in a package, to check if the package exists, is not the best way to tackle the problems, IMO.

      IMO base is a crap module, it tries to handle both loading external files and inline packages, but it tries Too Much Magic™ and in some cases it may fail. It's failed many times for me in the past. I don't trust it any more.

      Use it all the time. Never had a problem with it :-)

      I don't really think it's that magical. Two rules:

      • If the package is already loaded - use it
      • Otherwise, try and use it.
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re^2: Defining classes and inheritance using packages within a single .pl file, without creating modules
by MaxKlokan (Monk) on Nov 29, 2006 at 12:43 UTC
    Thank you for the good advice. For this particular case, IMHO the neatest way is to turn the { package ... } blocks into BEGIN { package ... } blocks, and remove the require Class1; line. If I don't remove that line, the block for Class1 is evaluated twice and I get a warning about new() and PrintHello() being redifined. The same warnings are issued if I use base qw(Class1); (without BEGIN). I guess that's because of the same reason (base implies a require). It seems therefore that use base is not the way to go for inline class definition.
      Well, require is for loading files, which you won't need at all if everything is inline, so removing it is a good idea.

      The reason I would prefer 'use base' (bart's objections notwithstanding) over assigning directly to @ISA is that I don't like having run-time code in modules. (It also seems to work fine in a test here - just remove the @Class2::ISA line and use base qw/Class2/; instead of require Class2;).

      I would say that having a script with a single entry point and execution following function/method invocation, it is easier to see what is going on. Otherwise one must read through the whole file to check for side-effects.

      For example, I commonly have a

      main</c< function: <code> #!/usr/bin/perl use warnings; use strict; exit !main(@ARGV); sub main { ... } sub foo { }
      The benefit of the early exit is that a maintainer can see that there isn't any run-time code lurking down between any later subs (since execution never flows down there). It also means that any lexical variables I use in main aren't accidentally accessible in the functions within the script. (OK, it might make sense to have some file-scope vars, but such things should stand out, not be the default because I'm writing code at the toplevel).

      Yes, for quick scripts etc it is handy to have the scripting behaviour, but by the time we bring OO or helper functions in, we probably have enough complexity that this kind of discipline is beneficial.

      So...when using perl as a programming language (as opposed to a 'scripting' language), I think it is harmful to have code at the top-level - it's effects are too wide-spread (variable scope too large) and the code can be spread over too large an area to see easily.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://586649]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (5)
As of 2024-04-25 19:59 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found