[Pdx-pm] Musings on operator overloading (was: File-Fu overloading)

Aaron Crane perl at aaroncrane.co.uk
Sun Feb 24 08:26:03 PST 2008


Aristotle Pagaltzis writes:
> [Cc to perl6-language as I think this is of interest]

[I agree with that, but since this response has nothing to do with
Perl 6 or its design, I'm deleting p6l again.]

> My counterproposal, were it possible in Perl 5, which it isn’t,
> would be something like this:
> 
>     my $dir = 'foo'; # no object at all!
> 
>     foreach my $fn (qw(bar baz bat)) {
>       my $file = path { $dir / $fn };
>       open my $fh, '<', $file or die "$!\n";
>       while(my $line = <$fh>) {
>         # ...
>       }
>     }

With only a little fiddling, that does seem at least mostly possible
to me.  Making the "no object at all!" bit work is obviously hardest;
overload::constant is the typical hammer for such nails, as long as
its deficiencies can be accepted.  The biggest deficiency here is that
you end up needing the appropriate overload::constant applied to every
string constant in your program.  Perhaps autoboxing could help with
that.

If that can of worms can be overlooked, scoped operator overloading
seems like it could be done something like this:

#! /usr/bin/perl

use strict;
use warnings;

use Test::More tests => 2;

use Sub::Name qw<subname>;

{
    package File;
    use base qw<Path::Class::File>;
    use overload '/' => 'slash';
    sub slash {
        my ($self, $rhs) = @_;
        my $class = ref $self;
        die "Division impossible on $class; did you mean path { ... }?"
            if !main::in_distinguished_scope();
        return $class->new($self->dir, $rhs, $self->basename);
        # ... or whatever semantics are deemed appropriate
    }
}

{
    my %in_scope;
    my $n;
    sub path (&) {
        my ($block) = @_;
        $n++;
        my $name = "File::.call.path.$n";
        subname($name, $block);
        local $in_scope{$name} = 1;
        $block->();
    }
    sub in_distinguished_scope {
        return $in_scope{ (caller 2)[3] };
    }
}


my $f = File->new(qw<a b foo.txt>);
my $d = Path::Class::Dir->new('c');

my $using_scoped_op = path { $f / $d };
is($using_scoped_op, File->new(qw<a b c foo.txt>),
   'correct result with scoped op');

my $using_global_op = eval { $f / $d };
my $err = $@;
cmp_ok(!defined $using_global_op, '&&', $err, 'no result with global op');

__END__

-- 
Aaron Crane


More information about the Pdx-pm-list mailing list