寻求Perl习语来检查$ self是一个类还是一个对象

Kra*_*lew 7 oop perl

在Perl中,我被一些看起来像下面的bug的东西咬了一口:

package Foo;
sub method {
    my $self = shift;
    my @args = @_;
    ...
}
Run Code Online (Sandbox Code Playgroud)

我把它称为子程序,而不是方法:

Foo::method( "arg1", "arg2" );
Run Code Online (Sandbox Code Playgroud)

而不是将其称为方法 - 在这种情况下,它是一个"类方法":

Foo->method( "arg1", "arg2" );
Run Code Online (Sandbox Code Playgroud)

调用Foo :: method("arg1","arg2")导致"arg1"被删除.

使用"对象方法"可能会出现类似的注意事项:

my $object = Foo->new();
$obj->method( "arg1", "arg2" );
Run Code Online (Sandbox Code Playgroud)

是否有一个友好,简洁的Perl习惯用于检查传统上调用的第一个参数$self实际上是类(包)和/或类/包名称中的对象?

我想出的最好的是:

package Foo;
sub method {
    my $self = ($_[0]->isa(__PACKAGE__) ? shift @_ : die "...error message...";
    my @args = @_;
    ...
}
Run Code Online (Sandbox Code Playgroud)

这并不简单

package Foo;
sub method {
    my $self = shift;
    die "...error message..."  if $self->isa(__PACKAGE__);
    my @args = @_;
    ...
}
Run Code Online (Sandbox Code Playgroud)

要么

package Foo;
use Carp::Assert;
sub method {
    my $self = shift;
    assert($self->isa(__PACKAGE__));
    my @args = @_;
    ...
}
Run Code Online (Sandbox Code Playgroud)

笔记:

我知道Perl签名,但不喜欢使用实验性功能.

我知道use attributes和:method.这是最好的方式吗?关于"不断发展"的特征的类似担忧.

我知道穆斯 - 但我不认为穆斯强制执行此操作.(我错过了什么.)

Perl的问题在于有很多方法可以做某事.

Sch*_*ern 10

最好的答案是不要在单个包中混合函数和方法.众所周知,"混合模块"存在问题.您可能希望创建函数的所有内容都应该是类方法调用.

在日常编程中几乎不需要完全限定函数调用.


最简洁的方法是使用Moops,这是使用语法糖的Moose的新方法.

use Moops;

class Foo {
    method something() {
        print("something called\n");
    }
}

Foo->new->something();
Foo::something();

# something called
# Invocant $self is required at /Users/schwern/tmp/test.plx line 10.
Run Code Online (Sandbox Code Playgroud)

Moops被标记为不稳定,但这是界面,而不是签名本身.签名已经存在并且可以在生产中使用很长时间,比它们内置的时间更长.更令人担心的是,一年多来没有发布,但是作者写了好东西.你的来电.


否则,与其他任何东西一样,写一个函数.

use Carp;
use Scalar::Util qw(blessed);

sub check_invocant {
    my $thing = shift;

    my $caller = caller;

    if( !defined $thing ) {
        croak "The invocant is not defined";
    }
    elsif( !ref $thing ) {
        croak "The invocant is not a reference";
    }
    elsif( !blessed $thing ) {
        croak "The invocant is not an object";
    }
    elsif( !$thing->isa($caller) ) {
        croak "The invocant is not a subclass of $caller";
    }

    return $thing;
}
Run Code Online (Sandbox Code Playgroud)

由于这会返回调用者并为您处理异常,因此可以非常简洁地使用它.

package Foo;

sub method {
    my $self = ::check_invocant(shift);

    ...
}
Run Code Online (Sandbox Code Playgroud)