Короче говоря: мы хотим пометить строки, чтобы позже мы могли что-то с ними делать, даже если они будут встроены в другие строки.
Итак, мы решили, эй, давайте попробуем перегрузить. Это довольно аккуратно. Я могу сделать что-то вроде:
my $str = str::new('<encode this later>');
my $html = "<html>$str</html>";
print $html; # <html><encode this later></html>
print $html->encode; # <html><encode this later></html>
Он делает это путем перегрузки оператора конкатенации для создания нового массива объектов с простой строкой «‹html>», оболочкой объекта «‹encode this Later>» и простой строкой «‹/html>». Он может вкладывать их произвольно. При кодировании он оставит простые строки, но закодирует строки объекта. Но если вы преобразуете объект в строку, он просто выдает все это в виде простых строк.
Это работает хорошо, за исключением того, что в некоторых случаях это приводит к искажению без видимой причины. В приведенном ниже сценарии показано поведение, которое я продублировал в версиях с 5.10 по 5.22.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Data::Dumper; $Data::Dumper::Sortkeys=1;
my $str1 = str::new('foo');
my $str2 = str::new('bar');
my $good1 = "$str1 $str2";
my $good2;
$good2 = $good1;
my($good3, $good4);
$good3 = "$str1 a";
$good4 = "a $str1";
my($bad1, $bad2, $bad3);
$bad1 = "a $str1 a";
$bad2 = "$str1 $str2";
$bad3 = "a $str1 a $str2 a";
say Dumper { GOOD => [$good1, $good2, $good3], BAD => [$bad1, $bad2, $bad3] };
$bad1 = ''."a $str1 a";
$bad2 = ''."$str1 $str2";
$bad3 = ''."a $str1 a $str2 a";
say Dumper { BAD_GOOD => [$bad1, $bad2, $bad3] };
package str;
use Data::Dumper; $Data::Dumper::Sortkeys=1;
use strict;
use warnings;
use 5.010;
use Scalar::Util 'reftype';
use overload (
'""' => \&stringify,
'.' => \&concat,
);
sub new {
my($value) = @_;
bless((ref $value ? $value : \$value), __PACKAGE__);
}
sub stringify {
my($str) = @_;
#say Dumper { stringify => \@_ };
if (reftype($str) eq 'ARRAY') {
return join '', @$str;
}
else {
$$str;
}
}
sub concat {
my($s1, $s2, $inverted) = @_;
#say Dumper { concat => \@_ };
return new( $inverted ? [$s2, $s1] : [$s1, $s2] );
}
1;
Я хочу, чтобы все они выводились как объекты, а не строки. Но все «ПЛОХИЕ» примеры зашифрованы. Все "ПЛОХИЕ" примеры - это когда я присваиваю строковый объект, который я в данный момент объединяю с ранее объявленной переменной. Если я объявляю в то же время или объединяю строки ранее, или добавляю дополнительную конкатенацию (помимо интерполированной строки concat), то это работает нормально.
Это безумие.
Результат скрипта:
$VAR1 = {
'BAD' => [
'a foo a',
'foo bar',
'a foo a bar a'
],
'GOOD' => [
bless( [
bless( [
bless( do{\(my $o = 'foo')}, 'str' ),
' '
], 'str' ),
bless( do{\(my $o = 'bar')}, 'str' )
], 'str' ),
$VAR1->{'GOOD'}[0],
bless( [
$VAR1->{'GOOD'}[0][0][0],
' a'
], 'str' )
]
};
$VAR1 = {
'BAD_GOOD' => [
bless( [
'',
bless( [
bless( [
'a ',
bless( do{\(my $o = 'foo')}, 'str' )
], 'str' ),
' a'
], 'str' )
], 'str' ),
bless( [
'',
bless( [
bless( [
$VAR1->{'BAD_GOOD'}[0][1][0][1],
' '
], 'str' ),
bless( do{\(my $o = 'bar')}, 'str' )
], 'str' )
], 'str' ),
bless( [
'',
bless( [
bless( [
bless( [
bless( [
'a ',
$VAR1->{'BAD_GOOD'}[0][1][0][1]
], 'str' ),
' a '
], 'str' ),
$VAR1->{'BAD_GOOD'}[1][1][1]
], 'str' ),
' a'
], 'str' )
], 'str' )
]
};
Поведение не имеет для меня никакого смысла. Я хотел бы понять, почему это работает таким образом, и я хотел бы найти обходной путь.