Почему `eq` не работает, когда один аргумент перегружает строчение?

Я понял (трудный путь), что оператор eq дает фатальную ошибку времени выполнения, когда один из операндов является объектом с перегруженной строкой.

Вот минимальный пример:

my $test = MyTest->new('test');
print 'yes' if $test eq 'test';

package MyTest;

use overload '""' => sub { my $self = shift; return $self->{'str'} };

sub new {
    my ( $class, $str ) = @_;
    return bless { str => $str }, $class;
}

Результат этого:

Operation "eq": no method found,
    left argument in overloaded package MyTest,
    right argument has no overloaded magic at ./test.pl line 7.

Мое ожидание от чтения perlop заключалось бы в том, что строковый контекст принудительно используется для обоих операндов, запуская метод строковой привязки в $test, затем сравниваются полученные строки, Почему это не работает? Что на самом деле происходит?

Контекст, в котором я столкнулся с этой проблемой, был в script, который использует как autodie, так и Try::Tiny. В блоке try я die с некоторыми конкретными сообщениями, которые нужно поймать. Но в блоке catch, когда я тестирую, является ли $_ eq "my specific message\n", это дает время выполнения, если $_ является autodie::exception.

Я знаю, что мне придется заменить $_ eq "..." на !ref && $_ eq "...", но я хотел бы знать, почему.

Ответы

Ответ 1

Вы перегружаете только перегрузку, а не сравнение строк. Однако прагма overload будет использовать перегруженную строковую строку для сравнения строк, если вы укажете параметр fallback => 1:

my $test = MyTest->new('test');
print 'yes' if $test eq 'test';

package MyTest;

use overload
    fallback => 1,
    '""' => sub { my $self = shift; return $self->{'str'} };

sub new {
    my ( $class, $str ) = @_;
    return bless { str => $str }, $class;
}

Подробная информация о том, почему это работает:

При передаче перегруженного объекта оператор eq попытается вызвать перегрузку eq. Мы не обеспечивали перегрузку, и мы не обеспечивали перегрузку cmp, из которой eq можно было автогенерировать. Поэтому Perl выдаст эту ошибку.

С fallback => 1 включено, ошибка подавлена, и Perl будет делать то, что он будет делать в любом случае, - принуждение аргументов к строкам (которое вызывает перегрузку строения или другую магию) и сравнить их.