http://qs321.pair.com?node_id=211105

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

Disclaimer: I'm a professional perl programmer who is being pushed into a Java project. Star-crossed lovers and all...

To get to the point: Is there a software project out there to give Java Perl power? I'm not talking about taking my perl code and stuffing it into Java, I'm talking about some org.perl.* packages that will reduce my coding time with lovely Java versions of perl beauties like map, (un)pack, printf, and grep. For example:

import org.perl.*; Collection foo = Perl5.unpack(template, string);
I'm sure some of my brethren have been forced to code in Java at times, and have searched for (or coded themselves) functions to make Java smell more like a camel. I'm just wondering if anyone who's been down this path before migt direct me toward the path they took, or if I should try to do it myself. Thank you in advance.

--
perl: code of the samurai

Replies are listed 'Best First'.
Re: Perl Functionality in Java
by djantzen (Priest) on Nov 07, 2002 at 18:26 UTC

    I don't know of any projects like this, but ++ to you samurai; I think we ought to do it, and to start us off here's a first run implementation of map, applicable to grep. Its weakness is that the loop iterations and return values must be defined by the user, however I'm not sure yet how to get similar behavior to setting $_ as in Perl. In any case, here goes:

    public class Mapper { // Define an interface that each block will implement. Note that // we use java.lang.Object here so this will work with any class. interface MapBlock { public Object[] run(Object[] list); } // Define our map method, which calls the methods declared in the // MapBlock interface and returns the outcome. public static Object[] map(MapBlock block, Object[] list) { Object[] out = block.run(list); return out; } // Define a main to test the idea. public static void main(String[] args) { // Test arguments, lowercase 'foo' and 'bar' Object[] objects = { new String("foo"), new String("bar") }; // Call the map function, passing an anonymous class implementin +g // interface MapBlock containing the code we want to run. Object[] new_list = map(new MapBlock() { public Object[] run(Object[] list) { Object[] out = new Object[list.length]; for (int i = 0; i < list.length; i++) { String str = (String)list[i]; // Create uppercase versions out[i] = str.toUpperCase(); } return out; } }, objects); // end of MapBlock, second param objects // Test to see what our new Object array contains. System.out.println((String)new_list[0] + (String)new_list[1]); } }

    prints "FOOBAR".

    Update: The fundamental concept at work here (and in the reply immediate below) is that of the anonymous class. An anonymous class may be declared in two ways:

    new interface-name () { class-body } // as in this node -or- new class-name ([argument-list]) { class-body } // as in the reply

    The call new MapBlock creates a new object that implements the interface MapBlock, which amounts to an is-a relationship. The class itself anonymous, but it's-a MapBlock. That means we can create a one-off class implementing particular behavior, and because the compiler is aware of the MapBlock interface, it'll let you create a new class that implements it. This is closest we can get AFAIK to passing a block of code to a function. The problem in this version though is that all the functionality must be packed into the anonymous implementation, including the code for iterating through the list, as an interface cannot include implementation. But that means this isn't yet like map.

    So, below I try the other option, subclassing another class. In this case new MapBlock creates an anonymous subclass of MapBlock, which means we can inherit a constructor and some implementation behavior. The idea now is to pack the implicit map functions, e.g., iterating over a list and placing the results into an array, into the superclass. The anonymous subclass now only has to implement the abstract method processElement(Object obj) so it can be called from the superclass's run method, and this gets us pretty close to the map BLOCK LIST behavior we want./Update.

    Now it's on to foreach, because I really despise the normal, braindead Java 'for' loops!

    cheers, fever

      Okay, I've got a major improvement on this. Rather than forcing the user to handle iteration through the loop and to compile the return list -- making this implementation of highly dubious value over a normal 'for' loop -- we define an abstract superclass from which to inherit rather an interface to implement:

      public abstract static class MapBlock { public Object[] run(Object[] list) { Object[] out = new Object[list.length]; // begin loop through list for (int i = 0; i < list.length; i++) { // call processElement on ourself and collect element return +ed Object new_obj = this.processElement(list[i]); out[i] = new_obj; } return out; } public abstract Object processElement(Object obj); // This might alternatively return an Object array for greater fle +xibility. // 'run' would require slight alterations to deal with an array co +ming back. // Thanks for the idea to [jdporter] }

      The original definition of map remains the same. Then below, we can simplify the anonymous class to the point where this is really starting to look like map!

      Object[] new_list = map(new MapBlock() { public Object processElement(Object obj) { String str = (String)obj; return str.toUpperCase(); } }, objects); // end of MapBlock, second param objects

      It's times like these I say booyah! ; )

Re: Perl Functionality in Java
by samtregar (Abbot) on Nov 07, 2002 at 19:52 UTC
    I think you're making a mistake trying to program in Java like you program in Perl. Each language has its own set of metaphors and idioms. To reach optimal coding velocity you will need to learn a new way of doing things. Trying to import one language's metaphors and idioms into another is only going to impede your progress.

    Case in point: C programmers that program Perl as though it were C with scalars. Of course they'd be twice as efficient if they realized they could replace all their for(;;) loops with foreach, map and grep, but they concentrate so hard on making Perl feel like C that they miss them completely.

    -sam

      Trying to import one language's metaphors and idioms into another is only going to impede your progress.

      If this were strictly true Perl would not exist. It is commonly recognized that the strength our beloved language is that it freely borrows from other languages those idioms, constructs, methodologies that have proved effective and expressive.

      You are correct in saying that often people new to Perl attempt to write in the form of their favorite language -- I did this in moving from Java to Perl -- and thereby risk severely limiting their command of Perl. However, the importation of powerful idioms such as map, grep, foreach, et al, in no way restricts the expressiveness of Java. Quite the contrary, they have the chance to cut through the verbosity and rigidity of standard Java, thereby making the language more usable.

        If this were strictly true Perl would not exist.

        Hogwash. The problem presented is one of an experienced programmer in language X starting a new project in language Y. This is entirely different from the problem of a language designer, expert in both languages X and Y, creating a new language Z.

        However, the importation of powerful idioms such as map, grep, foreach, et al, in no way restricts the expressiveness of Java. Quite the contrary, they have the chance to cut through the verbosity and rigidity of standard Java, thereby making the language more usable.

        It's a possiblity, but I think the risk outweighs the potential gain. This might work out well for an experienced Java hacker that already has all the standard Java idioms at his fingertips. Mixing in some Perl might be a big help. However, for a Perl geek trying to swim in the Java swamp, these imported idioms will essentially act as a crutch. Worse, they will render his code unmaintainable by normal Java programmers. All in all, I think this is best avoided.

        -sam

Re: Perl Functionality in Java
by zaimoni (Beadle) on Nov 08, 2002 at 05:45 UTC

    To get to the point: Is there a software project out there to give Java Perl power? I'm not talking about taking my perl code and stuffing it into Java, I'm talking about some org.perl.* packages that will reduce my coding time with lovely Java versions of perl beauties like map, (un)pack, printf, and grep. For example:

    <p>import org.perl.*;</p> <p>Collection foo = Perl5.unpack(template, string);</p>

    map, grep: Good idea for rapid prototyping. I see that one proposal is already in...just fine-tune it. There are several Java classes derived from an abstract list type.

    printf: This C-ism is ill-advised, even in Perl. I think of it as "C portability support". In the absence of a perfect analog, I would become familiar with the higher-level text I/O instead.

    pack/unpack: Java is designed to protect you from binary. Any pure Java implementation is likely to have the speed of molasses in July.

    There are analogs of the LWP module family in Java; I would be concentrating on that for high-level internet connectivity. For instance, an image can be specified with a URL source. It's interesting watching 14 images load asynchronously....

    I would also be careful with braces. If the coding style guidelines permit, you can use them to force temporary variables to go out of scope before the function exits. This is a RAM efficiency measure. The object here is not Perl-like code, but Perl-like memory recycling.

Re: Perl Functionality in Java
by John M. Dlugosz (Monsignor) on Nov 07, 2002 at 20:50 UTC
    A number of years ago at work I imlemented pack/unpack like things in C++. Indeed, it made dealing with network protocol data streams much simpler; it cut the code size by a very large factor.