Skip to content

builtin::reftype() should, like ref(), always return a string #23586

@PhilipGuenther

Description

@PhilipGuenther

Module: builtin

Description
One of the great things about ref() is that it always returns a
string, meaning that it's easy to use---even under 'warnings'---without
having to perform other tests on the argument or result first. Even
ref(undef) returns a defined value.

Contrast builtin::reftype(), which returns undef for non-references.
This makes it harder to use in code that may operate on mixed scalars
and references (tree walks, etc).

This also renders the advice in the UNIVERSAL(3) pod incorrect,
where it says:

 Previous versions of this documentation suggested using "isa"
 as a function to determine the type of a reference:

   $yes = UNIVERSAL::isa($h, "HASH");
   $yes = UNIVERSAL::isa("Foo", "Bar");

 The problem is that this code would never call an overridden
 "isa" method in any class.  Instead, use "reftype" from
 Scalar::Util for the first case:

   use Scalar::Util 'reftype';

   $yes = reftype( $h ) eq "HASH";

This works without warnings when $h isn't a reference:
$yes = UNIVERSAL::isa($h, "HASH");

This doesn't:
$yes = reftype( $h ) eq "HASH";

You have to use one of these instead:
$yes = ref($h) ne '' && reftype( $h ) eq "HASH";
$yes = defined reftype( $h ) && reftype( $h ) eq "HASH";
$yes = (reftype( $h ) // '') eq "HASH";
$yes = do { my $t = reftype( $h ); defined $t && $t eq "HASH" };

Having ref() return '' for non-references may be unexpected, but
it has great UX. reftype() should follow that.

Steps to Reproduce


use v5.40;

my($scalar, $undef) = ( 'foo' );

these are good

if (ref($scalar) eq 'HASH') { say "process hash"; }
if (ref($undef) eq 'ARRAY') { say "process array"; }

these warn

if (reftype($scalar) eq 'HASH') { say "process hash"; }
if (reftype($undef) eq 'ARRAY') { say "process array"; }

say "scalar = $scalar";

Expected behavior
Script should output "scalar = foo", but instead emits warnings to get this:

Use of uninitialized value $scalar in string eq at - line 10.
Use of uninitialized value $undef in string eq at - line 11.
scalar = foo

(side note: that first error is misleading/confusing to claim that
$scalar is uninitialized when it clearly contains 'foo')

Perl configuration

Site configuration information for perl 5.40.1:

Configured by root at Thu Jan  1  0:00:00 UTC 1970.

Summary of my perl5 (revision 5 version 40 subversion 1) configuration:
   
  Platform:
    osname=openbsd
    osvers=7.6
    archname=amd64-openbsd
    uname='openbsd'
    config_args='-dse -Dopenbsd_distribution=defined -Dmksymlinks'
    hint=recommended
    useposix=true
    d_sigaction=define
    useithreads=undef
    usemultiplicity=undef
    use64bitint=define
    use64bitall=define
    uselongdouble=undef
    usemymalloc=n
    default_inc_excludes_dot=define
  Compiler:
    cc='cc'
    ccflags ='-DNO_LOCALE_NUMERIC -DNO_LOCALE_COLLATE -DNO_LOCALE_NUMERIC -DNO_LOCALE_COLLATE -DNO_LOCALE_MONETARY -DNO_
LOCALE_TIME -DNO_LOCALE_MESSAGES -DLIBC_HANDLES_MISMATCHED_CTYPE -fno-strict-aliasing -fno-delete-null-pointer-checks -p
ipe -fstack-protector-strong -I/usr/local/include'
    optimize='-O2'
    cppflags='-DBIG_TIME -DNO_LOCALE_NUMERIC -DNO_LOCALE_COLLATE -DNO_LOCALE_NUMERIC -DNO_LOCALE_COLLATE -DNO_LOCALE_MON
ETARY -DNO_LOCALE_TIME -DNO_LOCALE_MESSAGES -DLIBC_HANDLES_MISMATCHED_CTYPE -fno-strict-aliasing -fno-delete-null-pointe
r-checks -pipe -fstack-protector-strong -I/usr/local/include'
    ccversion=''
    gccversion='OpenBSD Clang 16.0.6'
    gccosandvers=''
    intsize=4
    longsize=8
    ptrsize=8
    doublesize=8
    byteorder=12345678
    doublekind=3
    d_longlong=define
    longlongsize=8
    d_longdbl=define
    longdblsize=16
    longdblkind=3
    ivtype='long'
    ivsize=8
    nvtype='double'
    nvsize=8
    Off_t='off_t'
    lseeksize=8
    alignbytes=8
    prototype=define
  Linker and Libraries:
    ld='cc'
    ldflags ='-Wl,-E  -fstack-protector-strong -L/usr/local/lib'
    libpth=/usr/lib /usr/lib/clang/16/lib
    libs=-lm -lc
    perllibs=-lm -lc
    libc=/usr/lib/libc.so.100.3
    so=so
    useshrplib=true
    libperl=libperl.so.25.0
    gnulibc_version=''
  Dynamic Linking:
    dlsrc=dl_dlopen.xs
    dlext=so
    d_dlsymun=undef
    ccdlflags='-Wl,-R/usr/libdata/perl5/amd64-openbsd/CORE'
    cccdlflags='-DPIC -fpic '
    lddlflags='-shared -fpic  -fstack-protector-strong -L/usr/local/lib'


---
@INC for perl 5.40.1:
    /usr/local/libdata/perl5/site_perl/amd64-openbsd
    /usr/local/libdata/perl5/site_perl
    /usr/libdata/perl5/amd64-openbsd
    /usr/libdata/perl5

---
Environment for perl 5.40.1:
    HOME=/home/users/guenther
    LANG (unset)
    LANGUAGE (unset)
    LC_CTYPE=C.UTF-8
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/home/users/guenther/bin:/bin:/usr/bin:/usr/local/bin:/usr/local/sbin:/sbin:/usr/sbin:/usr/X11R6/bin:/usr/games
    PERL_BADLANG (unset)
    SHELL=/bin/ksh

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions