I've come up with an interlanguage comparison test based on a suggestion from someone else. The idea is, simply, to examine a fairly arbitrary set of metrics for three characteristics of a language: expressiveness, power, and flexibility. The metrics are, of course: succinctness as measured in distinct syntactic elements, list handling, and use of data in a largely arbitrary manner.

Requirements for the code to be at all meaningful for the test include readability, understandability for someone new to the language with only a brief explanation at most, and "correct" idiomatic style for the language.

My initial thought is to define the test as requiring input when calling the program and output without further interaction with the user (in other words, acting as a "filter"), doing something meaningful between input and output in a manner that maximizes demonstration of power and flexibility in a relatively simple operation. Toward that end, I figure a good test is a program that sends the input into a lexical closure, which operates on that data and sends it to STDOUT (with linebreaks) when it is called.

My question, then, is twofold. First, what (if anything) is a better option than the above test (and why)? Second, for the above described test, is there a better Perl example than the one below?

#!/usr/bin/perl -l sub foo { my @list = @_; sub { print for reverse @list }; } my $bar = foo(@ARGV); $bar->();

print substr("Just another Perl hacker", 0, -2);
- apotheon
CopyWrite Chad Perrin

Replies are listed 'Best First'.
Re: list reversal closure
by BrowserUk (Patriarch) on Aug 21, 2006 at 11:18 UTC

    Two immediate thoughts.

    1. Very few languages support lexical closures, so your test excludes many of them.
    2. Your test is a GUOLCAAS. A gratuitous use of lexical closures and anonymous subs--neither is a requirement for performing the function of the code which would be equally achieved by:
      #!/usr/bin/perl -l print for reverse @ARGV;
      • It is no more succinct than the simpler version.
      • It is no more correct than the much simpler version.
      • It is no more understandable than the simpler version.
      • It is no more efficient than the simpler version.
      • It is no more flexible that the simpler version.
      • It is no more reusable than the simpler version.
      • It is no more reliable that the simpler version.

    It's also rather limiting to write a filter program that only accepts input from the command line and not via redirection.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      While I agree with the thesis that appears to be implicit in your post (that only real-world problem solving can provide a truly comprehensive measure of a language's benefits), that's not terribly useful for discussion with "hard" data on hand. As such, I've gone a little closer to the scientific method approach by trying to work with a series of clearly defined, strictly regulated mini-tests that each focus on specific characteristics of languages while minimizing the problem of additional, unnecessary variables. As such, for this particular test, I'm looking for fewer untargeted variables in the test, not more of them.

      This unfortunately tends to result in a test whose conditions appear somewhat arbitrary and extraneous, until one considers the fact that the conditions are sorta the point.

      If, however, you think another approach than using @ARGV would be better, I certainly encourage you to modify my code (or write code from scratch) to improve upon my original, and if you have suggestions for addressing the needs of this little test directly as opposed to attempting to turn it into a real-world problem, I'm open to that as well.

      print substr("Just another Perl hacker", 0, -2);
      - apotheon
      CopyWrite Chad Perrin

        Put more simply, most languages assume the existence of command line arguments in an array, which eliminates the overhead of using the language or system libraries to process input for the test.

        That does treat some languages more fairly, but it's not really reflective of any real world condition. You might as well begin by having the language explicitly set an array or variable with a specified input.

        Side note on the closures bit -- a better, practical closures example is probably some sort of argument currying.

        sub first_n_chars { my $n = shift; return sub { return substr( shift, 0, $n ) }; } my $first_4 = first_n_chars(4); print $first_4->("hello world");

        -xdg

        Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: list reversal closure
by xdg (Monsignor) on Aug 21, 2006 at 11:02 UTC

    It's an interesting idea. But one that easily can be skewed towards any particular language's strengths (even unconsciously).

    I find this particular example fairly uninspiring, as I'm not sure when I would ever use such a thing. It feels like a completely arbitrary use of a closure to make it hard for languages that don't have such a thing. So, in short, I think it's too arbitrary. E.g. it could just as easily be a list object that overloads stringification to reverse the list. So to me, that makes the "list reversal closure" seems like an example with some unconscious bias.

    A better "test" is probably one that considers how a language performs (loosely speaking) on average across similar tasks each presented by advocates of a different language. The idea isn't to see how well one language performs on things it does well, but to see how well it performs on things that other languages do well.

    Even that is likely to be highly controversial and unsatisfying. But to the OP's point, I don't think any single test will reveal much of anything.

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      The closure requirement isn't really all that awful, if you think about it: it translates directly into two other requirements that seem quite reasonable, for most purposes, to impose. The first is lexical scoping (or equivalently protected scope) and the second is the ability to return a function from a function and pass it around in a variable, while still being able to execute it.

      I'm generally a little unfairly biased against a language that doesn't support closures, perhaps, but I'm not so sure that a bias against languages that don't support everything needed to construct closures is at all unfair (despite the fineness of that distinction). While I'm keen on closures, that doesn't mean that closures don't provide a simple, clean, succinct, and fair means of demonstrating other positive characteristics of programming languages. In other words, the bias is conscious, but (as far as I can tell) largely irrelevant in this case.

      As for the notion of comparing on a series of tasks, that's sorta what's happening that prompted me to come up with this in the first place. There's sort of an ongoing series of task-examples being dreamed up by me and a small number of others who make use of various languages for the enjoyable exercise of comparing implementations across languages. In fact, it was a Pythonista that suggested doing something involving list processing in the first place.

      Single tests can demonstrate good points, though. You simply have to ensure that everyone involved in the comparisons is honest in the analysis, including an analysis of the shortcomings of the specific test task in demonstrating much. For instance, a succinctness test on "hello world" programs certainly won't declare a "winner" between Perl and Python, but it does demonstrate to a useful degree the significant and unwieldy scaffolding overhead required by Visual Basic for the simplest of tasks.

      print substr("Just another Perl hacker", 0, -2);
      - apotheon
      CopyWrite Chad Perrin

Re: list reversal closure
by imp (Priest) on Aug 21, 2006 at 12:15 UTC
    I find examples like the following more useful:
    sub reverse_iterator { my @list = @_; my $i = $#list; sub { return if $i < 0; return $list[$i--]; }; } my $foo = reverse_iterator(@ARGV); while (defined ($_ = $foo->())) { print; }

      Since the loop stop when undefined is returned, the following is the same, but simpler:

      sub reverse_iterator { my @list = @_; sub { return pop @list; }; }

      Update: I prefer to signal the end of the list out of band to allow undefined values in @list. That why I prefer returning the value through an argument.

      sub reverse_iterator { my @list = @_; sub { return unless @list; $_[0] = pop @list; return 1; }; } my $foo = reverse_iterator(@ARGV); while ($foo->(my $val)) { print("$val\n"); }

      Update: I just noticed that you assigned to $_ without localizing it first. Don't do that!

      Update: s/shift/pop/

        So how about this as an alternative?:

        #!/usr/bin/perl -l sub revlist { my @list = @_; sub { pop @list }; } my $foo = revlist(@ARGV); print while local $_ = $foo->();

        Edit: Optionally, closure subroutine might look like this, of course:

        sub { return unless @list; pop @list; };

        print substr("Just another Perl hacker", 0, -2);
        - apotheon
        CopyWrite Chad Perrin

      Using your iterator, but with a slightly different invocation:
      my $foo = reverse_iterator("a",undef,"c"); while (my ($elem) = $foo->()) { print( defined( $elem ) ? $elem : "undef" ); }
Re: list reversal closure
by wazoox (Prior) on Aug 21, 2006 at 16:26 UTC
    Joel Spolsky wrote an interesting article on the subject : Can your programming language do this?
    He comes to this conclusion:
    Ok. I hope you're convinced, by now, that programming languages with first-class functions let you find more opportunities for abstraction, which means your code is smaller, tighter, more reusable, and more scalable.
    Is it the "someone else" you mentioned ? :)

      'Fraid not. I've read that particular essay (several times), along with one by Paul Graham that touches on much the same thing, but it was actually list handling that was brought up by the person with whom I was discussing languistic tests and not the use of lexical closures.

      I'd certainly be happy to have a conversation like this with Joel Spolsky at some point, though. The guy's full of good ideas.

      print substr("Just another Perl hacker", 0, -2);
      - apotheon
      CopyWrite Chad Perrin

Re: list reversal closure
by chromatic (Archbishop) on Aug 21, 2006 at 20:59 UTC
    ... understandability for someone new to the language with only a brief explanation at most...

    I can't see how that's a meaningful measure of anything interesting. Why not "For which any docs fail to contain the first-following non-consonant?"

      It doesn't measure something interesting. It makes it possible for people comparing examples from different languages to actually compare examples from different languages without having to necessarily learn a half-dozen or more new languages.

      print substr("Just another Perl hacker", 0, -2);
      - apotheon
      CopyWrite Chad Perrin

Re: list reversal closure
by ikegami (Patriarch) on Aug 21, 2006 at 14:47 UTC

    Nit:
    print for reverse @list;
    is equivalent to
    print reverse @list;
    when $, and $\ are equal (as they are by default).

      Nit to your nit :) Notice the -l on his shebang line.

      C:\test>perl -e" print for reverse 1.. 10" 10987654321 C:\test>perl -le" print for reverse 1.. 10" 10 9 8 7 6 5 4 3 2 1

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

        Thanks for bringing that up in response. You're right: eliminating the for() changes the intended behavior of the program.

        Now, I suppose, a new question has arisen: Should I intend for every list element to be printed on a separate line, or should I intend for the list to have its own line as in ikegami's example? It's not particularly critical either way, but perhaps there's an aesthetic benefit to be had from one or the other approach.

        print substr("Just another Perl hacker", 0, -2);
        - apotheon
        CopyWrite Chad Perrin

        You did not contradrict me. I did say as long as $, and $\ are equal. -l changes $\, but not $,. This actually makes the version without for more flexible!