In relation to this node on microlanguages, I've been working on implementing the subset features as several modules, such that they look as follows:
$data = /some data read from file, perl object/; $data = process_A( $data, @options ); $data = process_B( $data, @more_options );
In every case I've written so far, the data type object is always the first argument to these functions. The process functions are contained in their own modules, such as MyCode::ProcessA, MyCode::ProcessB, etc. These functions typically return a new data type object.

Now, in the aforementioned thread, someone suggested that objectifying this might make more sense:

$data = /some data read from file, perl object/; $data = $data->process_A( @options ); $data = $data->process_B( @more_options );
which is just as clean (if not cleaner). However, the number of 'process' functions that I might generate are not limited, and that means that every time I add a new module with a new process, I'd have to modify the data type code in addition to add this feature, which is a nuicance.

So, I'm wondering if there is a "stupid perl trick" that's sufficiently portable (between 5.005 and 5.6) such that when the process moduels are loaded, they can automagically load any appropriate functions into the data type method list, eg:

# data type object at this point has no 'process_c' method. use MyCode::ProcessC; # now it ought to.. $newdata = $data->process_c( @even_more_options );
I figure if anything, this will be something in the BEGIN block for the process modules and possibly an AUTOLOAD in the data type code, but I'm not sure what needs to be added. Any suggestions or am I asking for the impossble?

-----------------------------------------------------
Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
"I can see my house from here!"
It's not what you know, but knowing how to find it if you don't know that's important

Replies are listed 'Best First'.
Re: Possible Stupid Perl Trick - Importing method into object class?
by dws (Chancellor) on Dec 07, 2001 at 22:39 UTC
    However, the number of 'process' functions that I might generate are not limited, and that means that every time I add a new module with a new process, I'd have to modify the data type code in addition to add this feature, which is a nuisance.

    Sounds like you might need a smart AUTOLOAD. Consider a scenario where you have a registry of "process functions" on disk, where the registry is a flat file database with pairs of "function-name, filename" as tuples. When AUTOLOAD is triggered, it scans this registry to locate the file that corresponds with a process function. When it finds one, it loads the file into a string, then evals it (as a sub definition) into the dataset's namespace.

    This approach would let you (or others) write new process functions in files external to your main body of code. For extra credit, you could provide an option in your main script to use a different, "debug" registry that you would use for debugging new functions.

    Here's an untested fragment, pieced together from pages 468-471 of the Cookbook.

    sub AUTOLOAD { my $self = shift; my $attr = $AUTOLOAD; $attr =~ s/.*:://; # open the registry, search for $attr, # open assocated filename and load the # contents into $sub no strict 'refs'; *$attr = eval $sub; goto &$attr; }
    Good lord, I just used a goto.

      This is a good plan, however what do you do with a function that has a bug? I would be very loathe to do that kind of run-time evaluation of potentially tainted code in production.

      Better (and safer!) is to have everything there at compile-time, using import (as I said above).

      ------
      We are the carpenters and bricklayers of the Information Age.

      Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      Good lord, I just used a goto.

      If it makes you feel any better, the form you used isn't really even a goto, or at least not the way C/C++/Java programmers mean (i.e., the goto-LABEL form). Its use in AUTOLOAD subs is precisely what the goto-&NAME form was intended for.

      See this node: Would you use 'goto' here? for more on the topic.

      dmm

      While this will probably work, it ends up with the same problem from the original idea: you have to maintain a repository of new functions which may be overlooked. That is, if I add a new processing step, I have to remember to add it to this file to make sure it works. Sure, as opposed to creating a new object method (and also remembering to import the new processing module), this is probably a bit easier, but again, it doesn't appear to solve the original problem.

      (And no, it's not the goto at the end of the code nor the lack of strictness that makes me wary of this method :-).

      -----------------------------------------------------
      Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
      "I can see my house from here!"
      It's not what you know, but knowing how to find it if you don't know that's important

        While this will probably work, it ends up with the same problem from the original idea: you have to maintain a repository of new functions which may be overlooked.

        You could skip the repository, and so something like

        if ( -f "$attr.sub" ) { # load and eval "$attr.sub" goto &$attr; }
        I used a registry so that you could stage the debugging of new functions. If that isn't an issue for you, then don't use a registry. I've used a registry for something like this in years past, and have found it useful, but YMMV.

Re: Possible Stupid Perl Trick - Importing method into object class?
by dragonchild (Archbishop) on Dec 07, 2001 at 22:18 UTC
    Another, much cleaner method would be to modify the import() function. So, essentially, what would happen is that your Data::Type class would do an import from MyCode::ProcessC. MyCode::ProcessC would inherit from Exporter, but would overload the import() function. That import function would redefine the symbols being imported as being part of the Data::Type package. Presto, Changeo, Automagico!. :-)

    (As usual whenever I'm feeling lazy, the rest of the solution is left as an exercise for the reader.)

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

(tye)Re: Possible Stupid Perl Trick - Importing method into object class?
by tye (Sage) on Dec 07, 2001 at 23:34 UTC

    If you want to require "use MyCode::ProcessC" be added to the "data type" class, then that is just standard, run-of-the-mill Exporter.pm work with @EXPORT= qw( process_c ).

    But it sounds like you want the "use MyCode::ProcessC" to be somewhere else. Since you are useing and not requireing, you could put the exporting code into MyCode::ProcessC::import (rather than a BEGIN block), which means that it would be called multiple times if you use MyCode::ProcessC multiple times. But that might be a good thing if you want to be "general purpose".

    For example, you could require:     use MyCode::ProcessC( "DataType::Class" ); in the main script and have MyCode/ProcessC.pm contain:

    package MyCode::ProcessC; use strict; sub import { if( 2 != @_ ) { require Carp; Carp::croak( 'Usage: use ',__PACKAGE__, '( "DataType::Class" )' ); } my( $self, $class )= @_; for my $meth ( qw( process_c ) ) { no strict 'refs'; *{$class."::".$meth}= \&$meth; } }

            - tye (but my friends call me "Tye")
      This is definitely along the lines of what I'm looking for, and tested to make sure that it works with no problem. However, this is a large chuck of code that would need to be included in every possible process module.

      I've been trying to think of a way to place this in a parent class that the other process classes can inherit from, but there would seem to be a problem with the order of new, import, and the like that that I can't seem to find a way to get this to work.

      Again, VERY hypothetical code:

      package My::ProcessC; BEGIN { my @processfuncs = qw ( processC processC_alt ); } use My::ProcessParent qw(register);
      Alternatively, I'm wondering if modifying dws's idea above might be worth exploring. That is, add a register function to the DataType class, which stores what names are associated with with package and function, then have, in the process module's begin block, call that global-level function to register code. Then use AUTOLOAD in the DataType to handle those type of classes.

      -----------------------------------------------------
      Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
      "I can see my house from here!"
      It's not what you know, but knowing how to find it if you don't know that's important

        Well another alternative I proposed elsewhere was:

        package My::ProcessC; BEGIN { push @DataType::ISA, __PACKAGE__; }
        This is "reverse inheritance" (:

                - tye (but my friends call me "Tye")
        What's wrong with putting the import() function in some Process::Base class and having all your My::ProcessC whatevers inherit from it?

        ------
        We are the carpenters and bricklayers of the Information Age.

        Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

Re: Possible Stupid Perl Trick - Importing method into object class?
by t'mo (Pilgrim) on Dec 08, 2001 at 00:54 UTC
    The process functions are contained in their own modules, such as MyCode::ProcessA, MyCode::ProcessB, etc.

    Why?

    Or, more in line with my thinking, and assuming that there's a good chance you'll use objects to implement this, why not put all your functions/methods in a single file? (That excludes an object hierarchy, however. I would think that if you had a hierarcy of objects, then would probably want separate files.)