У меня есть API, который изначально принимал простое постоянное целое число, но я расширяю его, чтобы вместо этого его можно было вычислить при использовании, передав вместо этого ссылку на подпрограмму. Чтобы сохранить обратную совместимость, я проверяю, является ли это ссылкой на код:
'CODE' eq ref($var)
? $var->()
: $var;
но я понял, что исключаются объекты, действующие как подпрограммы. Что-то вроде этого:
package Foo {
sub new { bless {}, 'Foo' }
use overload '&{}' => sub { sub { "Hello, world." } };
}
Я мог бы просто попытаться вызвать его внутри блока eval
, но тогда мне пришлось бы тщательно проверить $@
, чтобы определить, не произошел ли сбой, потому что он не был ссылкой на подпрограмму (таким образом, возвращаясь к простому скаляру) по сравнению с вызванной подпрограммой. по какой-то причине (ошибка должна распространяться).
В этом случае, конечно, поскольку старое значение не было ссылкой, я мог бы просто предположить, что любая ссылка может быть вызвана, но мне нужно общее решение (поскольку я использовал этот подход раньше, когда он был хэш-ссылкой ). Есть ли простой и надежный способ проверить, является ли скаляр «исполняемым»?
Вы можете проверить это, попытавшись разыменовать его, но не запускать как подпрограмму.
my $is_code = ref $ref && do { local $@; eval { $ref = \&$ref; 1 } };
В этом случае вам все равно, что это за ошибка, важно, произошла ли она. \&$ref
достаточно, но, назначив результат обратно $ref
, вы можете избежать повторного вызова перегрузки при вызове этого кода. Локализация $@
просто из вежливости.
Как используется в мой модуль Autoload::AUTOCAN, с исходной ссылкой на haarg.
Теперь он возвращает false для вещей, которые можно рассматривать как подпрограммы, и может возвращать true для вещей, которые являются целыми числами. (Если вы хотите поддерживать перегруженную субификацию, вы наверняка захотите поддерживать перегруженную нумерацию)
@ikegami Если вы имеете в виду благословенные ссылки, которые также можно использовать как целые числа, конечно, вы ничего не можете с этим поделать, проводя это различие. Но я не знаю, что «можно рассматривать как подпрограмму» не пройдет проверку.
Символические ссылки. .
@ikegami Я собираюсь сказать, что принятие символической ссылки - это то, что ни один API не должен пытаться сделать или мог бы сделать в здравом уме.
Нет, Perl API не должен принимать что-то, что может быть либо целым числом, либо подчиненным.
в идеальном мире да :)
@ysth Это просто ужасно (интерфейс int-or-code) в любом мире, не так ли? Я бы сказал, тем более в реальном мире.
На самом деле вполне нормально, если вы не заходите так далеко в DWIM, проблема заключается в попытке угадать, что пользователь хотел передать. Принимайте только неблагословенный coderef и рассматривайте все остальное как целое число, вы можете использовать простую проверку ref в OP, не давайте пользователю никаких ожиданий другого поведения. Никаких сложностей, никакой двусмысленности.
Использование полиморфизма на основе типов изначально не работает в Perl. Вот почему
keys($ref)
и smartmatching — неудачные эксперименты. Например, что если у вас есть объект, который перегружает и нумерацию, и&{}
? У вас даже может быть подпрограмма с именем123
, в которой123
становится действительной ссылкой на подпрограмму.