(Message perl/six/blah:567) showproc perl/six/blah/567 /home/mjd/bin/mailpager /home/mjd/MH-Mail/perl/six/blah/567 Return-Path: mjd@plover.com Return-Path: Delivered-To: mjd-filter-deliver@plover.com Received: (qmail 27964 invoked by uid 119); 19 Dec 2000 20:49:00 -0000 Delivered-To: mjd-filter@plover.com Received: (qmail 27951 invoked by uid 119); 19 Dec 2000 20:48:57 -0000 Delivered-To: mjd@plover.com Received: (qmail 27946 invoked by uid 119); 19 Dec 2000 20:48:57 -0000 Message-ID: <20001219204857.27945.qmail@plover.com> To: damian@conway.org cc: mjd@plover.com Subject: RFC224 Organization: Plover Systems Date: Tue, 19 Dec 2000 15:48:57 -0500 From: Mark-Jason Dominus This is similar to your RFC 224. I like it better in some ways, although the migration issues are more problematic than with your proposal. I would be grateful for any comments you might have. =head1 TITLE Better way to determine object class =head1 VERSION Maintainer: Dominus Date: 19 December 2000 Version: 1 Mailing List: perl6-internals, perl6-language Number: 1 =head1 ABSTRACT The Perl ref() function is defective because it conflates two inequivalent notions: A data item's class, and its type. This causes coding problems and reduces code reliability. I propose that the ref() function be deprecated and replaced with class() and type(). =head1 DESCRIPTION The Perl ref() function returns the 'type' of a scalar. If the scalar is a reference to a blessed object, the stash name is returned. Otherwise, if the scalar is a reference to an unblessed object, a string like 'HASH' or 'ARRAY' is returned. Otherwise a false value is returned. =head2 The problem This semantics was a bad decision. It makes most common tasks difficult to accomplish. For example, suppose you have a scalar and you want to know if it is a hashref. The intent was for if (ref $ref eq 'HASH') { ... } to work. But it doesn't, because if $ref is a blessed hashref, you get the class name back instead of the string 'HASH'. Even if you are trying to find out of $ref is an *unblessed* hashref, the code above might not work, because $ref might refer to an array that has been blessed into a package named 'HASH'. Recognizing this deficiency, code was added to UNIVERSAL::isa to allow $ref->isa('HASH') to return true whenver $ref is a hash reference, whether or not $ref inherits from the HASH class. This just increases the confusion between classes and types. It is also not useful for recognizing hash references because it generates a fatal run-time error if $ref is an unblessed hash reference. Probably the only reliable way to accomplish this simple task is to use something like this: if (defined($ref) && eval { exists $ref->{""}; 1 }) { ... } Suppose you want to know if your scalar contains an object. There is no good way to do that either. One (silly) choice is: if (ref $ref !~ /^(?:SCALAR|ARRAY|HASH|GLOB|CODE|LVALUE|REF|FORMAT|IO|UNKNOWN)$/) { ... } Again, the 'ref' function simply isn't useful. You must resort to something like this: if (eval { $self->can('can') }) { ... } The ref() function has some other minor oddities which, although they are not problems in practice, are indications that something is wrong. For example, the commonly-used if (ref $s) { ... } test (advertised in the manual) will not work if $s is an object blessed into a package named '0'. =head2 Proposed solution That the two functions of 'ref' should be separated. Perl 6 will have a new 'class' function which returns the class of an object, and undefined if its argument is a nonreference or an unblessed reference. There should be a corresponding function 'type' which returns the underlying data type of a reference, regardless of whether it is blessed or not. ref() can be retained for compatibility, or can be eliminated entirely. (See MIGRATION below.) The code at universal.c:98-110 that enables the ->isa hack should be eliminated. =head1 IMPLEMENTATION The naughty behavior of ref() appears to derive from the analogous confusion in Perl_sv_reftype at the C level. This function should be eliminated. Sample (untested and unchecked) implementations for type() and class(), and a reimplementation of reftype(), follow: PP(pp_type) { djSP; dTARGET; SV *sv; char *pv; sv = POPs; if (sv && SvGMAGICAL(sv)) mg_get(sv); if (!sv || !SvROK(sv)) RETPUSHNO; sv = SvRV(sv); pv = sv_reftype(sv); PUSHp(pv, strlen(pv)); RETURN; } PP(pp_class) { djSP; dTARGET; SV *sv; char *pv; sv = POPs; if (sv && SvGMAGICAL(sv)) mg_get(sv); if (!sv || !SvROK(sv)) RETPUSHNO; sv = SvRV(sv); if (!SvOBJECT(sv) RETPUSHNO; pv = HvNAME(SvSTASH(sv)); PUSHp(pv, strlen(pv)); RETURN; } char * Perl_sv_reftype(pTHX_ SV *sv) { switch (SvTYPE(sv)) { case SVt_NULL: case SVt_IV: case SVt_NV: case SVt_RV: case SVt_PV: case SVt_PVIV: case SVt_PVNV: case SVt_PVMG: case SVt_PVBM: if (SvROK(sv)) return "REF"; else return "SCALAR"; case SVt_PVLV: return "LVALUE"; case SVt_PVAV: return "ARRAY"; case SVt_PVHV: return "HASH"; case SVt_PVCV: return "CODE"; case SVt_PVGV: return "GLOB"; case SVt_PVFM: return "FORMAT"; case SVt_PVIO: return "IO"; default: return "UNKNOWN"; } } =head1 MIGRATION ISSUES A Perl 5 program that contains a function named 'class' or 'type' will need modification in the translation stage. It should be sufficient for the translator to transform all mentions of 'class' and 'type' to the corresponding fully-qualified names. If 'ref' is eliminated, it may be translated by the Perl 5 -> Perl 6 translator as ref(EXPR) => do { my $_val = EXPR; my $_class = class($_val); defined($_class) ? $_class : type($_val) } In many cases it will be better for the programmer to simply replace ref() with whichever of class() or type() is appropriate for the circumstances. The tradeoffs for fixing the UNIVERSAL->isa misfeature are less clear. One possibility is that the translator can detect cases in which the argument to isa is known at compile time, and translate $o->isa('HASH') to (type $o eq 'HASH') . But this fails in at least two circumstances: When the argument to 'isa' is determined at run time: sub is_made_of { my ($object, $type) = @_; $object->isa($type); } and when the ->isa method is overridden. The best solution here may simply be to leave the strange behavior intact. =head1 REFERENCES RFC 6: "Standard Event Loop" perlvar manpage for discussion of %SIG