3

I have following code:

my $coderef = ${MyModule::MyTool->new};

but when I try

$coderef->();

i got error:

Not a CODE reference

How can I take reference to constructor (without calling it) and run referenced code late?

jesper
  • 879
  • 8
  • 21

3 Answers3

12

The ${...} is the scalar dereference operator, not the anonymous subroutine constructor. You want:

my $coderef = sub {MyModule::MyTool->new};

And if your constructor takes arguments, you could write it this way:

my $coderef = sub {MyModule::MyTool->new(@_)};

The two examples above do not address one issue, and that is preserving the functionality of caller. If your constructor needs this (many do not), you can use Perl's magic goto &sub syntax:

my $coderef = sub {unshift @_, 'MyModule::MyTool'; goto &{$_[0]->can('new')} };

That probably requires a little explanation. First, the module name is placed before any other arguments (which is what the new method will expect). Then I used the UNIVERSAL method ->can to retrieve the coderef for the new method. goto &{...} then jumps to that coderef using the current argument list.

EDIT: The comments below show that there is some confusion as to when you would need to use the longer third technique. Here is a short segment that shows the problem:

package Original;
    sub new {say +(caller)[0]}  # presumably some better use of caller
                                # or Carp (which uses caller)

package Encapsulate::Simple;
    sub new {
        my (undef, $invocant, $method) = @_;
        sub {$invocant->$method(@_)}
    }

package Encapsulate::Better;
    sub new {
        my (undef, $invocant, $method) = @_;
        sub {unshift @_, $invocant; goto &{$invocant->can($method)}}
    }

package main;

my $bad = Encapsulate::Simple->new(qw/Original new/);

$bad->(); # always prints 'Encapsulate::Simple'

my $good = Encapsulate::Better->new(qw/Original new/);

$good->(); # prints 'main' as it should

package another;

$bad->();  # erroneously prints 'Encapsulate::Simple' again
$good->(); # prints 'another' as it should

So in short, Encapsulate::Better's sub preserves the exact functionality of Original->new whereas Encapsulate::Simple permanently binds it to its package, breaking any encapsulated methods that use caller for anything.

Eric Strom
  • 39,821
  • 2
  • 80
  • 152
  • But I don't want to call this constructor. I only need to get reference to it. I want to create array of code references to some constructors in order to call them later. – jesper Nov 19 '10 at 21:42
  • @jesper, the anonymous sub does not call the constructor until it is executed with `$coderef->();` – friedo Nov 19 '10 at 21:45
  • 1
    @jesper => you can't just take a reference to a method and expect it to work by itself. The reason for this is that most methods need to be passed their invocant (be it the module name or an object blessed into that module). This means that you need to curry one argument onto the coderef. The simplest way to do that is written above. But a more verbose way would be `my $coderef = do {my $method = MyModule::MyTool->can('new'); sub {$method->('MyModule::MyTool', @_)}};` – Eric Strom Nov 19 '10 at 21:53
  • Hellyeah, the issue with caller is what I've missed. Now it works great! – jesper Nov 19 '10 at 21:55
  • 1
    @Eric, that’s a pretty — um, “exotic” — way of writing `$funcptr = sub { MyModule::MyTool->new(@_) };`, eh? :( – tchrist Nov 19 '10 at 22:44
  • @tchrist => what do you mean? That is exactly what I have for my second example. I then show an example that preserves `caller` for the method, which of course is a bit longer. – Eric Strom Nov 19 '10 at 22:58
  • @Eric, The “more verbose way” does nothing the other doesn’t, but does so in a less obvious fashion. What am I missing? – tchrist Nov 19 '10 at 23:01
  • @tchrist => say `MyModule::MyTool->new` contains some code that uses `caller` for anything. With the shorter method, there is an intervening call frame that may screw things up (particularly if the coderef is made in a different package). That is why I used the `goto &sub` syntax. it is the same idea as re-dispatching to Exporter with `sub import {goto &{Exporter->can('import')}}` rather than inheriting from `Exporter` – Eric Strom Nov 19 '10 at 23:06
  • @Eric, The only thing that comes immediately to mind on the caller’s package is `Symbol::qualify` and `Symbol::qualify_to_ref`, used so that you can take a string representing a name in the caller’s package and figure out which package the handle is really in. Speaking of avoiding imports, have you ever noticed how scandalously many objects `->can("Carp")` ? :( – tchrist Nov 19 '10 at 23:12
  • @tchrist => I am not sure exactly what you mean with regard to `Symbol`, but take a look at my latest edit for an example showing the difference in functionality. – Eric Strom Nov 19 '10 at 23:45
  • @Eric, thanks;your edit makes it nice and clear. You’re probably too young to remember people passing filehandles as bareword strings instead of globs. `Symbol` is a way to try to cope with that mess. It also provides `Symbol::gensym`, which gets you a new filehandle that doesn’t have a screwed up refcount, from back before autoviving handles. Look at the source for `Symbol::gensym` to see what I mean. – tchrist Nov 19 '10 at 23:50
1

Use \& to obtain a reference to a named function:

my $coderef = \&MyModule::MyTool::new;
mob
  • 117,087
  • 18
  • 149
  • 283
  • But will calling that code as `$coderef->();` pass the module name as first argument, as some constructors may expect? – aschepler Nov 19 '10 at 21:38
  • @aschepler - No it will not. Check out Eric Strom's answer instead. – mob Nov 19 '10 at 21:41
  • I think it doesn't - this is why this code doesn't work for me. – jesper Nov 19 '10 at 21:44
  • 1
    You need an invocant. A method always needs an invocant. A nekkid coderef an invocant isn’t. Put the coderef on the RHS of the arrow. It does not belong on the LHS. – tchrist Nov 19 '10 at 23:02
-1

This should work regardless of which package holds the new:

my $coderef = MyModule::MyTool->UNIVERSAL::can( 'new' ); 

So that if MyModule::MyTool does not implement their constructor, you can still get it's handle.

Axeman
  • 29,660
  • 2
  • 47
  • 102
  • 2
    That still doesn't pass the class name when you use `$coderef->()`. – cjm Nov 19 '10 at 21:58
  • @cjm: Agreed but it's 1) a codref to the constructor and 2) it will very likely not get "Not a CODE reference". It addresses the stated problem. I think he would learn quickly that he wants a closure instead of the actual reference. – Axeman Nov 19 '10 at 22:11
  • 1
    Isn't the problem that people try to put a methref on the LHS of the invocation arrow instead of on the RHS where it belongs? Don’t do `$coderef->(args)`, do `invocant->$coderef(args)`. – tchrist Nov 19 '10 at 22:38
  • @tchrist => I was playing around with an object that overloads `&{}` and the `invocant->$overloaded(...)` syntax did not seem to work properly. Do you know if that's a bug? – Eric Strom Nov 19 '10 at 22:59
  • You shouldn't ever call `can()` in that form -- consider if a class overrides the implementation of `can`. – Ether Nov 27 '10 at 20:43