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

(perhaps an "Uncool Uses for Perl could turn into RFCs for Perl 7?)

The Perl Core Language lacks builtin mechanisms for templating and programmatic block manipulation, both of which are necessary adjuncts to Perl's builtin support for higher-order functions.

Eval takes a string or a block. Of the two, only Perl strings can be added to or deleted from programmatically. Compare this with Lisp. In Lisp, eval takes a list and interprets it as a function call. The list can be added to or deleted from programmatically. Furthermore, the deletions can be made structurally, not in terms of strings. By structural, I mean that you can work at the level of program sentences not by mucking about with a bunch of hairy string manipulations. E.g., it is very easy to alter an evaluate-able block in Lisp, to say, get rid of the 3rd sentence in the eval-block, or to get rid of every sentence which makes a function to 'print. But with Perl, you have no hope of manipulating its evaluate-able and manipulable unit (the string) this way, without hairy and error-prone and full-of-exception string manipulation.

With that, I present a few lines of code that I wrote to do higher-order programming in Perl. The idea is that I am writing a program that connects to a series of FTP server, waits until something happens on any of the FTP servers and then does something else on the same FTP server.

Both something happens and something else are variant, but the cycling over hosts wasn't. So the high-level way to handle this is to make something happens and something else variables, in this case evaluatable Perl string. And submit them to a function which keeps trying until something happens and then does something else

my $until_no_upfile = "!ftp_file_exists ( '!!FTP_HOST!!', '$ftp::user', '$ftp::pass', +'$ftp::dir', '$script::upfile_name' );"; my $then_upload; map { $then_upload .= "ftp_upload ( '!!FTP_HOST!!', '$ftp::user', ' +$ftp::pass', '$ftp::dir', '$_' ); " } ($script::order_file, $script: +:upfile_name); host_cycle($until_no_upfile, $then_upload);

sub host_cycle

sub host_cycle { my ($_until,$_then)=@_; my $error_count; { for my $ftp_host (@ftp::host) { warn "ftp_host $ftp_host of @ftp::host"; my $until = $_until ; $until =~ s/!!FTP_HOST!!/$ftp_host/g; my $then = $_then ; $then =~ s/!!FTP_HOST!!/$ftp_host/g; warn "eval $until... ORIGINAL: **$_until**"; defined(my $retval = eval $until) || die "error: $@"; warn "retval $retval"; if (!$retval) { warn " * Unsuccessful"; next; } else { warn " * SUCCESSFUL"; warn "eval $then... ORIGINAL: **$_then**"; defined(my $retval = eval $then) || die "error: $@"; warn "retval $retval"; return 1; } } sleep $host_cycle::sleep; redo if (++$error_count < $host_cycle::retries); } }

Replies are listed 'Best First'.
Re: Templating and Programmatic Block Manipulation in Perl
by merlyn (Sage) on Sep 28, 2000 at 17:41 UTC
    Behavior re-use in Perl is most often handled as it is in other languages that don't have eval: with object inheritance and with pluggable designs (passing coderefs for variant behavior). The advantage to avoiding eval is that all code is compiled at compile time. No need for lisp's degree of introspection.

    -- Randal L. Schwartz, Perl hacker

      Aha!

      I thought about your comments above. And I have a prototype for how it works.
      package HostCycle; $ftp_host = undef; sub until { ; } sub then { ; } sub cycle {my $self=shift; for $ftp_host (@ftp_host) { my $retval = $self->until($ftp_host); if ($retval) { $self->then($ftp_host); } else { ++$error_count; } package SendOrder; @ISA=qw(HostCycle); sub until { # Wait for upfile to disappear } sub then { # Upload orderfile and upfile } package GetOrder @ISA=qw(HostCycle}; sub until { # Wait for downfile to disappear } sub then { # Download all files in directory }

      And of course regarding runtime alteration of code blocks: when was the last time you needed that? Answer: when you are trying to create your model as metaphorical to the language as opposed to creating a model with the language. Douglas Hofstadter in "Goedel, Escher, Bach: The Eternal Golden Braid" tried to say that we needed self-modifying, self-reflective languages. But in actually we just need to ability to modify and study structures.

Re: Templating and Programmatic Block Manipulation in Perl
by mirod (Canon) on Sep 28, 2000 at 18:29 UTC

    Ok, so I think this is a bad idea:

    • security-wise the use of string eval is nearly always risky, depending on the way you use the script of course, but I think it is a nasty habit anyway
    • LISP tends to be completely interpreted, hence modifying the code at run time gives no added penalty, while Perl code is pre-compiled, which is a Good Thing (TM) for performance reasons. I hope it stays this way

    Now that I think about it, if your eval was in a BEGIN block at least the eval syntax would be checked at compile time

      As a user of Lisp, I'd like to point out that while thirty years ago Lisp may have been completely interpreted, this is certainly not true today. One may work with interpreted code while developing, but will almost certainly compile before actually using it.