Sign In/My Account | View Cart  
advertisement


Listen Print

Apocalypse 3
by Larry Wall | Pages: 1, 2, 3, 4, 5, 6

Editor's Note: this Apocalypse is out of date and remains here for historic reasons. See Synopsis 03 for the latest information.

RFC 025: Operators: Multiway comparisons

Previous Apocalypses

Apocalypse 1

Apocalypse 2

This RFC proposes that expressions involving multiple chained comparisons should act like mathematician would expect. That is, if you say this:

    0 <= $x < 10

it really means something like:

    0 <= $x && $x < 10

The $x would only be evaluated once, however. (This is very much like the rewrite rule we use to explain assignment operators such as $x += 3.)

I started with this RFC simply because it's not of any earthshaking importance whether I accept it or not. The tradeoff is whether to put some slight complexity into the grammar in order to save some slight complexity in some Perl programs. The complexity in the grammar is not much of a problem here, since it's amortized over all possible uses of it, and it already matches the known psychology of a great number of people.

There is a potential interaction with precedence levels, however. If we choose to allow an expression like:

    0 <= $x == $y < 20

then we'll have to unify the precedence levels of the comparison operators with the equality operators. I don't see a great problem with this, since the main reason for having them different was (I believe) so that you could write an exclusive of two comparisons, like this:

    $x < 10 != $y < 10

However, Perl has a built-in xor operator, so this isn't really much of an issue. And there's a lot to be said for forcing parentheses in that last expression anyway, just for clarity. So unless anyone comes up with a large objection that I'm not seeing, this RFC is accepted.

RFC 320: Allow grouping of -X file tests and add filetest builtin

This RFC proposes to allow clustering of file test operators much like some Unix utilities allow bundling of single character switches. That is, if you say:

    -drwx $file

it really means something like:

    -d $file && -r $file && -w $file && -x $file

Unfortunately, as proposed, this syntax will simply be too confusing. We have to be able to negate named operators and subroutines. The proposed workaround of putting a space after a unary minus is much too onerous and counterintuitive, or at least countercultural.

The only way to rescue the proposal would be to say that such operators are autoloaded in some fashion; any negated but unrecognized operator would then be assumed to be a clustered filetest. This would be risky in that it would prevent Perl from catching misspelled subroutine names at compile time when negated, and the error might well not get caught at run time either, if all the characters in the name are valid filetests, and if the argument can be interpreted as a filename or filehandle (which is usually). Perhaps it would be naturally disallowed under use strict, since we'd basically be treating -xyz as a bareword. On the other hand, in Perl 5, all method names are essentially in the unrecognized category until run time, so it would be impossible to tell whether to parse the minus sign as a real negation. Optional type declarations in Perl 6 would only help the compiler with variables that are actually declared to have a type. Fortunately, a negated 1 is still true, so even if we parsed the negation as a real negation, it might still end up doing the right thing. But it's all very tacky.

So I'm thinking of a different tack. Instead of bundling the letters:

    -drwx $file

let's think about the trick of returning the value of $file for a true value. Then we'd write nested unary operators like this:

    -d -r -w -x $file

One tricky thing about that is that the operators are applied right to left. And they don't really short circuit the way stacked && would (though the optimizer could probably fix that). So I expect we could do this for the default, and if you want the -drwx as an autoloaded backstop, you can explicitly declare that.

In any event, the proposed filetest built-in need not be built in. It can just be a universal method. (Or maybe just common to strings and filehandles?)

My one hesitation in making cascading operators work like that is that people might be tempted to get cute with the returned filename:

    $handle = open -r -w -x $file or die;

That might be terribly confusing to a lot of people. The solution to this conundrum is presented at the end of the next section.

RFC 290: Better english names for -X

This RFC proposes long names as aliases for the various filetest operators, so that instead of saying:

    -r $file

you might say something like:

    use english;
    freadable($file)

Actually, there's no need for the use english, I expect. These names could merely universal (or nearly universal) methods. In any case, we should start getting used to the idea that mumble($foo) is equivalent to $foo.mumble(), at least in the absence of a local subroutine definition to the contrary. So I expect that we'll see both:

    is_readable($file)

and:

    $file.is_readable

Similar to the cascaded filetest ops in the previous section, one approach might be that the boolean methods return the object in question for success so that method calls could be stacked without repeating the object:

    if ($file.is_dir
             .is_readable
             .is_writable
             .is_executable) {

But -drwx $file could still be construed as more readable, for some definition of readability. And cascading methods aren't really short-circuited. Plus, the value returned would have to be something like ``$file is true,'' to prevent confusion over filename ``0.''

There is also the question of whether this really saves us anything other than a little notational convenience. If each of those methods has to do a stat on the filename, it will be rather slow. To fix that, what we'd actually have to return would be not the filename, but some object containing the stat buffer (represented in Perl 5 by the _ character). If we did that, we wouldn't have to play $file is true games, because a valid stat buffer object would (presumably) always be true (at least until it's false).

The same argument would apply to cascaded filetest operators we talked about earlier. An autoloaded -drwx handler would presumably be smart enough to do a single stat. But we'd likely lose the speed gain by invoking the autoload mechanism. So cascaded operators (either -X style or .is_XXX style) are the way to go. They just return objects that know how to be either boolean or stat buffer objects in context. This implies you could even say

    $statbuf = -f $file or die "Not a regular file: $file";
    if (-r -w $statbuf) { ... }

This allows us to simplify the special case in Perl 5 represented by the _ token, which was always rather difficult to explain. And returning a stat buffer instead of $file prevents the confusing:

    $handle = open -r -w -x $file or die;

Unless, of course, we decide to make a stat buffer object return the filename in a string context. :-)

Pages: 1, 2, 3, 4, 5, 6

Next Pagearrow