nothingmuch has asked for the wisdom of the Perl Monks concerning the following question:

Howdy...

I'm trying to use interface, which is nicer than base if you don't trust yourself.

What it does is, in theory, is when it's import is called, it loads the calling module completely (using eval "use $foo"), making sure that it doesn't recurse into it's own import, and then when the calling module is loaded, it checks that all the methods in all the interfaces are implemented by the calling package or one of it's ancestors.

What happens in practice is that eval "use $pkg" goes into $pkg's import, and then exits from there, without finishing the compilation of the code in "${pkg}.pm".

One solution that works in general but breaks in some cases (especially BEGIN { require_ok("module"}, module->import }) is to delay interface checking from BEGIN time to CHECK time by building a queue. In the require_ok case, what happens is that OK is given to the harness, and then the test dies if the interface is incomplete. Not fun.

Is there any way to get the calling package's file to do something when it's done parsing (aside from source filters, which I think is even more flakey and overkill than delaying to CHECK)?

Is there anyway to use the recursive approach and get it to actually parse, and not return immediately?

Update: an example...

Given two files, Bar.pm:

package Bar use interface Foo; warn "blaah"; sub bar { } 1
and Foo.pm:
package Foo; sub bar {} 1
somewhere in @INC, perl -e 'require Bar'; does not print "blaah", but dies, saying that Bar doesn't implement a 'bar' like Foo requires, and furthermore perl -de 'require Bar'; traces through interface's eval "use $pkg", but warn "blaah"; is not encountered (even if it is wrapped in a BEGIN{} block).

Thanks!

-nuffin
zz zZ Z Z #!perl

Replies are listed 'Best First'.
Re: eval "use $module" doesn't
by Joost (Canon) on Aug 03, 2004 at 11:20 UTC
    Hmmmmmm. I found a workaround:
    package Bar warn "blaah"; sub bar { } use interface Foo; 1
    works as advertised.

    Further more, what interface is doing is essentially:

    package Bar; sub BEGIN { eval "use Bar" if $loaded_for_the_first_time; do_stuff(); }
    And expects Bar to be fully loaded when do_stuff() is called. This just isn't true; use Bar will only load, compile and execute Bar.pm if it hasn't been use'd before, but in this case it clearly is.

    This is the "right" behaviour, because otherwise this would go into an infinite loop:

    # Foo.pm use Bar; # Bar.pm use Foo;
    In other words, this is a bug in interface.

    update: s/import/BEGIN/;

Re: eval "use $module" doesn't
by fergal (Chaplain) on Aug 03, 2004 at 13:10 UTC
    There's another discussion of interface in interface.pm explained

    What's wrong with building a list of things to check and then checking them at CHECK time? It seems easier to get right than fiddling around with evals. For example, interface forgets to check $@ for errors after it's eval so if a module has a syntax error you will probably get an error from interface complaining about missing functions when what you really want is something telling you about the syntax error.

      Doing things at CHECK time limits your module to being used during the initial compile of a script. People like to be able to require or eval "use" modules later than that.
        That's fair enough I suppose. Ideally perl would treat CHECK and INIT blocks as a queue which can be filled up during any compile and are emptied out again after the compile, rather than giving "too late for INIT" messages. Perl 6 perhaps.