Я бы не рекомендовал этого делать. Вы вводите «действие на расстоянии», что приводит к очень трудным для обнаружения ошибкам. Пользователь думает, что он получает строку. Лексическую строку можно изменить, только изменив ее прямо и явно. Это должно быть изменено на месте или явно передано в функцию или ссылку, прикрепленную к чему-то.
my $text = $file->text;
say $text; # let's say it's 'foo'
...do some stuff...
$file->text('bar');
...do some more stuff...
# I should be able to safely assume it will still be 'foo'
say $text;
Этот блок кода легко понять, потому что все, что может повлиять на $text
, сразу видно. Это и есть лексический контекст, изолирующий то, что может изменить переменную.
Возвращая вещь, которая может измениться в любой момент, вы незаметно нарушили это предположение. Для пользователя нет никаких указаний на то, что предположение было нарушено. Когда они отправляются печатать $text
и получают bar
, неясно, что изменилось $text
. Что угодно во всей программе могло измениться $text
. Этот небольшой блок кода теперь бесконечно сложнее.
Другой способ взглянуть на это таков: скалярные переменные в Perl имеют определенный интерфейс. Часть этого интерфейса говорит, как их можно изменить. Вы ломаете этот интерфейс и лжете пользователю. Вот как обычно злоупотребляют перегруженными/связанными переменными.
Какую бы проблему вы ни пытались решить, вы решаете ее, добавляя новые проблемы, делая код более сложным и трудным для понимания. Я бы отступил назад и спросил, какую проблему вы пытаетесь решить с помощью связывания.
Вместо этого я бы просто вернул скалярную ссылку. Это предупреждает пользователя о том, что его можно изменить из-под них в любое время. Никакой магии, чтобы скрыть очень важную информацию.
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
{
package File;
use Moose;
has 'text_ref' => (
is => 'rw',
isa => 'Ref',
default => sub {
return \("");
}
);
sub BUILDARGS {
my $class = shift;
my %args = @_;
# "Cast" a scalar to a scalar ref.
if( defined $args{text} ) {
$args{text_ref} = \(delete $args{text});
}
return \%args;
}
sub text {
my $self = shift;
if( @_ ) {
# Change the existing text object.
${$self->text_ref} = shift;
return;
}
else {
return $self->text_ref;
}
}
}
my $file = 'File'->new('text' => 'hello');
my $text = $file->text();
say $$text;
$file->text('goodbye');
say $$text;
Тем не менее, вот как вы делаете то, что хотите.
Я бы не рекомендовал использовать галстук. Он очень медленный, значительно медленнее, чем вызов метода, глючный и причудливый. Одна из его особенностей заключается в том, что связанный характер привязывается к самой переменной, а не к данным, на которые ссылаются. Это означает, что вы не можете вернуть связанную переменную.
Вместо этого я бы рекомендовал использовать перегруженный объект для хранения вашего изменяющегося текста.
{
package ChangingText;
# Moose wants class types to be in a .pm file. We have to explciitly
# tell it this is a class type.
use Moose::Util::TypeConstraints qw(class_type);
class_type('ChangingText');
use overload
'""' => sub {
my $self = shift;
return $$self;
},
fallback => 1;
sub new {
my $class = shift;
my $text = shift;
return bless \$text, $class;
}
sub set_text {
my $self = shift;
my $new_text = shift;
$$self = $new_text;
return;
}
}
У перегруженных объектов есть свои предостережения, в основном из-за кода, который ожидает, что строки будут писать что-то вроде if !ref $arg
, но с ними легче справиться, чем с ошибками глубокой связи.
Чтобы сделать это прозрачным, сохраните объект ChangingText в объекте File, а затем поместите вокруг него созданный вручную метод доступа text
для обработки простых строк. Аксессор обязательно повторно использует один и тот же объект ChangingText.
Чтобы завершить иллюзию, BUILDARGS используется для изменения аргументов инициализации обычного текста в объект ChangingText.
{
package File;
use Moose;
has 'text_obj' => (
is => 'rw',
isa => 'ChangingText',
default => sub {
return ChangingText->new;
}
);
sub BUILDARGS {
my $class = shift;
my %args = @_;
# "Cast" plain text into a text object
if( defined $args{text} ) {
$args{text_obj} = ChangingText->new(delete $args{text});
}
return \%args;
}
sub text {
my $self = shift;
if( @_ ) {
# Change the existing text object.
$self->text_obj->set_text(shift);
return;
}
else {
return $self->text_obj;
}
}
}
Тогда он работает прозрачно.
my $file = File->new('text' => 'hello');
my $text = $file->text();
say $text; # hello
$file->text('goodbye');
say $text; # goodbye
person
Schwern
schedule
06.08.2015
$text
можно изменить вне контекста, в котором оно объявлено. Читатель не может знать, как$text
может измениться. Хуже того, читатель поверит, что$text
— это простая строка, и решит, что она чисто лексическая. Все это бесконечно усложняет отладку кода. Возможно, вам следует задать вопрос о проблеме, которую вы пытаетесь решить с помощью связывания? - person Schwern   schedule 06.08.2015