Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check if a Perl scalar can be treated as a subroutine?

I have an API that originally took just a plain constant integer, but I am extending it so it can instead be computed when used by passing a subroutine reference instead. To keep backwards compatibility, I'm testing if it's a code reference:

'CODE' eq ref($var)
    ? $var->()
    : $var;

but I realized that excludes objects that act as subroutines. Something like this:

package Foo {
    sub new { bless {}, 'Foo' }
    use overload '&{}' => sub { sub { "Hello, world." } };
}

I could just try to call it inside an eval block, but then would have to carefully check $@ to determine if it failed because it wasn't a subroutine reference (thus falling back to it being a simple scalar) vs. the called subroutine failed for some reason (error should be propagated).

In this case, of course, since the old value was a non-reference, I could just presume any reference is callable, but I'd like a general solution (as I've used this approach before when it used to be a hashref). Is there a simple and reliable way to test if a scalar is “executable”?

like image 807
derobert Avatar asked May 29 '19 21:05

derobert


Video Answer


1 Answers

You can check by attempting to dereference it, but not run it, as a subroutine.

my $is_code = ref $ref && do { local $@; eval { $ref = \&$ref; 1 } };

In this case you don't care what the error is, just whether one occurred. \&$ref is sufficient, but by assigning the result back to $ref you can avoid invoking the overload a second time when you call that coderef. Localizing $@ is just for politeness.

As used in my Autoload::AUTOCAN module, with original credit to haarg.

like image 112
Grinnz Avatar answered Sep 22 '22 08:09

Grinnz