Why does `eq` not work when one argument has overloaded stringification?
Asked Answered
P

1

17

I have realised (the hard way) that operator eq gives a fatal runtime error when one of the operand is an object with overloaded stringification.

Here is a minimal example:

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;
}

The result of running this is:

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

My expectation from reading perlop would be that string context is forced on both operands, firing the stringification method in $test, then the resulting strings are compared. Why doesn't it work? What is actually hapenning?

The context in which I had this problem was in a script that uses both autodie and Try::Tiny. In the try block, I die with some specific messages to be caught. But in the catch block, when I test for whether $_ eq "my specific message\n", this gives a runtime if $_ is an autodie::exception.

I know I will have to replace $_ eq "..." with !ref && $_ eq "...", but I would like to know why.

Pterodactyl answered 1/5, 2014 at 11:51 Comment(3)
try "$test" eq 'test'Schaeffer
@HunterMcMillen, that worked, thanks. But I don't see what difference it makes to the interpreter!Pterodactyl
eq comparison doesn't actually force $test to be stringified, it just use string compare on its arguments.Schaeffer
M
23

You only overloaded stringification, not string comparison. The overload pragma will however use the overloaded stringification for the string comparison if you specify the fallback => 1 parameter:

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;
}

Details on why this works:

When handed an overloaded object, the eq operator will try to invoke the eq overload. We did not provide an overload, and we didn't provide a cmp overload from which eq could be autogenerated. Therefore, Perl will issue that error.

With fallback => 1 enabled, the error is suppressed and Perl will do what it would do anyway – coerce the arguments to strings (which invokes stringification overloading or other magic), and compare them.

Metathesize answered 1/5, 2014 at 12:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.