Запретить интерпретации строк как дескриптор файла.

Perl имеет функцию, в которой строки, названные как дескриптор файла, считаются дескриптором файла:

# let this be some nice class I wrote
package Input {
    sub awesome { ... }
}

Итак, когда мы делаем Input->awesome или очень осторожно: 'Input'->awesome, метод будет вызван. Если только:

# now somewhere far, far away, in package main, somebody does this:
open Input, "<&", \*STDIN or die $!;  # normally we'd open to a file

Этот код даже не должен выполняться, но только его видит только для того, чтобы Perl интерпретировал строку 'Input' как дескриптор файла с этого момента. Поэтому вызов метода 'Input'->awesome умрет, потому что объект, представляющий дескриптор файла, не имеет замечательных методов.

Поскольку я только контролирую свой класс, но не другого кода, я не могу просто решить использовать только лексические дескрипторы файлов везде.

Есть ли способ, которым я могу заставить Input->awesome всегда быть вызовом метода в пакете Input, но никогда не обрабатывать файл (по крайней мере, в областях, контролируемых мной)? Я думаю, что не должно быть никакого столкновения имен, потому что пакет Input на самом деле является типом %Input::.

Полный код для воспроизведения проблемы (см. также ideone):

use strict;
use warnings;
use feature 'say';

say "This is perl $^V";

package Input {
    sub awesome {
        say "yay, this works";
    }
}

# this works
'Input'->awesome;

# the "open" is parsed, but not actually executed
eval <<'END';
    sub red_herring {
        open Input, "<&", \*STDIN or die $!;
    }
END
say "eval failed: [email protected]" if [email protected];

# this will die
eval {
    'Input'->awesome;
};
say "Caught: [email protected]" if [email protected];

Пример вывода:

This is perl v5.16.2
yay, this works
Caught: Can't locate object method "awesome" via package "IO::File" at prog.pl line 27.

Ответы

Ответ 1

Использование одного и того же идентификатора для двух разных вещей (используемый класс и дескриптор файла) вызывает проблемы. Если ваш класс используется из другого класса, который используется в коде, который использует дескриптор файла, ошибка не появляется:

My1.pm

package My1;

use warnings;
use strict;

sub new { bless [], shift }
sub awesome { 'My1'->new }

__PACKAGE__

My2.pm

package My2;

use warnings;
use strict;
use parent 'My1';

sub try {
    my $self = shift;
    return ('My1'->awesome, $self->awesome);
}

__PACKAGE__

script.pl

#!/usr/bin/perl
use warnings;
use strict;

use My2;
open My1, '<&', *STDIN;
my $o = 'My2'->new;
print $o->awesome, $o->try;

Ответ 2

Использование bareword Input в качестве дескриптора файла является нарушением соглашения о присвоении имен, чтобы иметь только заглавные буквы для FILEHANDLE и заглавных букв за Class es и Package s.

Кроме того, lexcial $filehandles были введены и поощрены уже очень давно.

Таким образом, программист, использующий ваш класс, явно плохо себя ведет, и поскольку пространства имен соответствуют определению глобального, это вряд ли может быть рассмотрено Perl (поддерживающее утверждение chorobas о попрошайничестве для проблем).

Некоторые соглашения об именах имеют решающее значение для всех (динамических) языков.

Спасибо за интересный вопрос, хотя в первый раз, когда я вижу вопрос Perl в SO, я предпочел бы видеть на perlmonks!:)

ОБНОВЛЕНИЕ: обсуждение здесь углублено: http://www.perlmonks.org/?node_id=1083985