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

I'm looking for some heavy magic here. I came up with this question while pursuing a very bad approach to a problem, but I wonder if it could be done.

Let's say that I have a reference. For the sake of argument, let's not (yet) talk about what it points to.
my $ref;

I'd like to be able to do things like this to it:
$ref->{key}; %$ref->{key}; %{$ref}->{key}; $$ref{$key};

All looks good so far, right? Looks, sounds, and tastes like a hash ref. Okay, I'd also like to be able to do stuff like this to it:
$ref->[0]; @$ref->[0]; @{$ref}->[0]; $$ref[0];

...and...
$$ref = 1; $$ref;

So, basically, I'm wondering if I could use any combination of OO, tied variables, symbol table entries, etc. to be able to do something like this:
my $ref = POLYMORPH->new( qw( key in some init values) ); $ref->{key}; # might return 'in' $ref->[0]; # might return 'key' $ref; # depends on implementation

I got as far as being able to tie a variable to a class (such as a subclass of Tie::Hash and/or Tie::Array). But, it seems that Perl already knows what kind of variable got tied, and dereferencing that variable as a particular type doesn't have any (obvious) hooks that I could manipulate.

I thought about mucking with some symbol table entries, but I'm not sure that would get me anywhere, since I'm not using named variables here -- I'm using a blessed reference returned from a constructor. That's pretty specific.

So, anybody out there want to take a stab at this? In case you got lost, the question is this:
How can I make a single reference that can be LEGALLY DEREFERENCED as a hash ref, array ref, AND a scalar ref, with the resulting operation tied to a class implementation?

Or, if anyone could point me at the piece of documentation that clearly states that this is patently impossible (and why), that would sate my curiosity just as well.

Thanks!
dpmott

Replies are listed 'Best First'.
(jeffa) Re: I'm looking for some 'heavy magic'
by jeffa (Bishop) on Jan 23, 2002 at 22:59 UTC
    Personally, every time i thought that i needed such a tool, i realized that really i didn't. :)

    As for the possibilities, check out Dominus's ArrayHashMonster.

    UPDATE: sample code!

    use strict; use ArrayHashMonster; my %hash; my @arry = (0..4); @hash{('A'..'E')} = @arry; my $monster = ArrayHashMonster->new( sub {$arry[$_[0]]}, sub {$hash{$_[0]}}, ); print $monster->[3],"\n"; print $monster->{D},"\n";

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: I'm looking for some 'heavy magic'
by Dominus (Parson) on Jan 23, 2002 at 23:31 UTC
    Asks dpmott:
    How can I make a single reference that can be LEGALLY DEREFERENCED as a hash ref, array ref, AND a scalar ref, with the resulting operation tied to a class implementation?
    Probably the easiest thing is to use a glob:
    my $thing; { $thing = local *GLOB; } $$thing = 20; @$thing = (1,2,3); %$thing = ('apple'=>'red', 'banana'=>'yellow'); # or use $thing->[...] and $thing->{...} # or tie as usual: tie %$thing => Hashclass, ...; tie @$thing => Arrayclass, ...; tie $$thing => Scalarclass, ...;
    The biggest drawback of this approach is that you can't bless the resulting $thing, because it isn't really a reference. But perhaps you don't care to do so.

    Hope this helps.

    --
    Mark Dominus
    Perl Paraphernalia

Re: I'm looking for some 'heavy magic'
by goldclaw (Scribe) on Jan 23, 2002 at 23:27 UTC
    Well, of course it is possible. The key module to use is overload. You can the overload the differnt reference access methods. Now, since bless needs a reference to something, I haven't been able to figure out how to overload all dereferencing operations at the same time. Anyway here is the code:
    package Mutator; use overload '%{}'=>\&as_hash, '${}'=>\&as_scalar, '@{}'=>\&as_array; sub new{ my $class=shift; my $scalar; #To get be able to create a scalar ref. #This is where we store the real values. We use a closeure #below, so %ref will be associated with our anon. sub. #If you want to do some init of the variables, you can #do it here. my %ref=(hash=>{}, array=>[], scalar=>\$scalar, ); # Now this is the sub that returns the actual reference # that we use. We call it in the overloaded methods. my $sub=sub { my $arg=shift; return $ref{$arg}; }; #bless the anon. sub into this class return bless $sub, $class; } #Get hash ref sub as_hash{ my $s=shift; return $s->("hash"); } #Get scalar ref sub as_scalar{ my $s=shift; return $s->("scalar"); } #get array ref sub as_array{ my $s=shift; return $s->("array"); } 1;

    An questions?

    GoldClaw

Re: I'm looking for some 'heavy magic'
by Anonymous Monk on Jan 24, 2002 at 10:41 UTC
    @$ref->[0]

    This is actually illegal Perl code, but works mysteriously anyway. It's a known bug, described in perldelta. It's applicable for hashes too.
      Howdy!

      Not quite.

      The actual examples in perldelta are:

      @x->[2] scalar(@x)->[2]
      "@$ref->[0]" can be parsed as @{$ref->[0]}, where $ref refers to an array whose first element is an arrayref.

      yours,
      Michael

        Nuh-uh!

        @$ref->[0] can be parsed as @{$ref->[0]}

        That's not what's happening.

        From perlref:
        Because of being able to omit the curlies for the simple case of $$x, people often make the mistake of viewing the dereferencing symbols as proper operators, and wonder about their precedence.

        Example code:
        use strict; my $r = [qw/foo bar/]; print $r->[0]; # 'foo' print @$r->[0]; # 'foo', due to bug. print @{$r->[0]}; # Can't use string ("foo") as an ARRAY ref...
        I bet that you've several times have heard "use references as variable names". That is a good way to describe references, and it works here too. Having $r = \@x, @$r->[0] will be the same as @x->[0]. So it is indeed the same issue as in perldelta.