Sunday afternoon hack: calling object methods on ARRAY and HASH refs

Andy Wardley abw at wardley.org
Sun Jun 15 17:30:18 BST 2008


Out of idle curiosity, I was wondering how hard it would be to make perl do
what TT does when you try and call a method on a list or hash reference.
That is, look for a method defined somewhere else (virtual methods in TT
speak).

Turns out it's not hard at all - only a dozen lines or so (diff against 5.10
below).  This maps method calls on array refs to the ARRAY package, and hash
refs to HASH.

Here's an example:

   package ARRAY;

   sub text {
       my $list  = shift;
       my $joint = @_ ? shift : ', ';
       join($joint, @$list);
   }

   package HASH;

   sub text {
       my $hash   = shift;
       my $joint  = @_ ? shift : ', ';
       my $spliff = @_ ? shift : ' => ';
       join($joint, map { $_ . $spliff . $hash->{ $_ } } keys %$hash);
   }

   package main;

   my $list = [ 1.618, 2.718, 3.142 ];
   print $list->text, "\n";

   my $hash = { phi => 1.618, e => 2.718, pi => 3.142 };
   print $hash->text, "\n";

The output:

   1.618, 2.718, 3.142
   e => 2.718, phi => 1.618, pi => 3.142

It doesn't break anything as far as I can tell, other than changing a
"Can't call method on an unblessed reference" error to a "Can't locate
object method xxxx in package HASH" error in the case of a method not
being defined.

Patch is below.  Can't vouch for the goodness of it (one known deficiency
is that can() doesn't work as it should, but there are probably plenty more).
It was just a quick hack as a proof-of-concept.  But hey, I think it's kinda
cool to see Perl's list and hash refs looking a bit more like "real" objects.
Your appreciation for that particular shade of shiny may of course vary.

A



--- perl-5.10.0/pp_hot.c	2007-12-18 10:47:08.000000000 +0000
+++ perl-5.10.0-abw-unblessed-methods/pp_hot.c	2008-06-15 17:00:37.000000000 +0100
@@ -3052,7 +3052,18 @@
      }

      /* if we got here, ob should be a reference or a glob */
-    if (!ob || !(SvOBJECT(ob)
+    if (ob && SvOBJECT(ob)) {
+        stash = SvSTASH(ob);
+    }
+    else if (ob && SvTYPE(ob) == SVt_PVAV) {
+        /* methods called on array references are mapped to ARRAY package */
+        stash = gv_stashpvn("ARRAY", 5, GV_ADD);
+    }
+    else if (ob && SvTYPE(ob) == SVt_PVHV) {
+        /* ditto for hash refs in HASH package */
+        stash = gv_stashpvn("HASH", 4, GV_ADD);
+    }
+    else if (!ob || !(SvOBJECT(ob)
  		 || (SvTYPE(ob) == SVt_PVGV && (ob = (SV*)GvIO((GV*)ob))
  		     && SvOBJECT(ob))))
      {
@@ -3061,8 +3072,6 @@
  		   name);
      }

-    stash = SvSTASH(ob);
-
    fetch:
      /* NOTE: stash may be null, hope hv_fetch_ent and
         gv_fetchmethod can cope (it seems they can) */


More information about the london.pm mailing list