Действительно ли проверка аргументов функции Perl стоит того?

Существует много шума о MooseX:: Метод:: Подписи и даже перед этим, модули, такие как Params:: Проверьте, которые разработаны для ввода, проверяют каждый аргумент методам или функциям. Я рассматриваю использование первого для всего своего будущего кода Perl, и персонального и в моем месте работы. Но я не уверен, стоит ли это усилия.

Я думаю обо всем коде Perl, который я видел (и записал), прежде чем это не выполнит такой проверки. Я очень редко вижу, что модуль делает это:

my ($a, $b) = @_;
defined $a or croak '$a must be defined!';
!ref $a or croak '$a must be a scalar!";
...
@_ == 2 or croak "Too many arguments!";

Возможно, потому что это - просто слишком много работы без некоторого модуля помощника, но возможно потому что на практике мы не отправляем избыточные аргументы функциям, и мы не отправляем arrayrefs в методы, которые ожидают скаляры - или если мы делаем, мы имеем use warnings; и мы быстро слышим об этом - утка, вводящая подход.

Таким образом, тип Perl проверяет стоящий хита производительности, или его силы преимущественно проявлены на скомпилированных, языках со строгим контролем типов, таких как C или Java?

Я интересуюсь ответами от любого, у кого есть опыт при записи Perl, который использует эти модули и видел преимущества (или не) от их использования; если Ваша компания/проект имеет какие-либо политики, имеющие отношение к проверке типа; и любые проблемы с проверкой типа и производительностью.

ОБНОВЛЕНИЕ: Я прочитал интересную статью о предмете недавно, названный Сильным Тестированием по сравнению со Строгим контролем типов. Игнорируя небольшую предвзятость Python, это по существу указывает, что проверка типа может задыхаться в некоторых случаях, и даже если Ваша программа проходит проверки типа, это не гарантия правильности - надлежащие тесты являются единственным способом быть уверенными.

18
задан Nathan Kleyn 13 April 2013 в 21:02
поделиться

5 ответов

В основном я согласен с Брайаном. То, насколько сильно вам нужно беспокоиться о входных данных вашего метода, в значительной степени зависит от того, насколько сильно вы обеспокоены тем, что а) кто-то введет плохие данные и б) плохие данные нарушат цель метода. Я бы также добавил, что существует разница между внешними и внутренними методами. Вам нужно быть более внимательным к публичным методам, потому что вы даете обещание потребителям вашего класса; и наоборот, вы можете быть менее внимательным к внутренним методам, поскольку у вас больше (теоретического) контроля над кодом, который обращается к ним, и вы можете винить только себя, если что-то пойдет не так.

MooseX::Method::Signatures - это элегантное решение для добавления простого декларативного способа объяснения параметров метода. Method::Signatures::Simple и Params::Validate хороши, но в них отсутствует одна из наиболее привлекательных для меня особенностей Moose: система типов. Я использовал MooseX::Declare и, соответственно, MooseX::Method::Signatures в нескольких проектах и обнаружил, что барьер для написания дополнительных проверок настолько минимален, что это почти соблазнительно.

7
ответ дан 30 November 2019 в 07:44
поделиться

Контраргумент, который я видел в ответ на это, заключается в том, что проверка параметров при каждом вызове функции избыточна и является пустой тратой процессорного времени. Сторонники этого аргумента выступают за модель, в которой все входящие данные строго проверяются, когда они впервые попадают в систему, но внутренние методы не имеют проверки параметров, потому что они должны вызываться только кодом, который будет передавать им данные, которые уже прошли проверку на границе системы, поэтому предполагается, что они все еще действительны.

Теоретически, мне нравится, как это звучит, но я также вижу, как легко это может рухнуть как карточный домик, если кто-то использует систему (или система должна вырасти, чтобы позволить использование) таким образом, который был непредвиденным при установлении первоначальной границы проверки. Достаточно одного внешнего вызова внутренней функции, и все ставки сделаны".

На практике я сейчас использую Moose, а Moose не дает возможности обойти валидацию на уровне атрибутов, к тому же MooseX::Declare обрабатывает и валидирует параметры метода с меньшей суетой, чем разворачивание @_ вручную, так что это практически спорный вопрос.

4
ответ дан 30 November 2019 в 07:44
поделиться

Если вам важно убедиться, что аргумент - это именно то, что вам нужно, оно того стоит. Производительность имеет значение только тогда, когда у вас уже есть правильная работа. Неважно, насколько быстро вы получите неправильный ответ или дамп ядра. :)

Это звучит глупо, но рассмотрим некоторые случаи, когда это не так. Неужели меня волнует, что здесь находится в @_ ?

sub looks_like_a_number { $_[0] !~ /\D/ }
sub is_a_dog            { eval { $_[0]->DOES( 'Dog' ) } }

В этих двух примерах, если аргумент не соответствует вашим ожиданиям, вы все равно получите правильный ответ, потому что недопустимые аргументы не будут пройти тесты. Некоторые люди считают это уродливым, и я понимаю их точку зрения, но я также считаю, что альтернатива уродлива. Кто победит?

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

Когда я думаю об условиях охраны, я думаю о том, что может случиться, если входные данные плохие, и насколько я забочусь о сбое. Я должен судить об этом по требованиям каждой ситуации. Я знаю, что это отстой в качестве ответа, но мне он, как правило, нравится больше, чем подход, основанный на рабстве и дисциплине, когда вы должны пройти через весь беспорядок, даже когда это не имеет значения.

Я боюсь Params :: Validate , потому что его код часто длиннее, чем моя подпрограмма. Материал Moose очень привлекателен, но вы должны понимать, что это способ заявить о том, что вы хотите, и при этом получить то, что вы можете построить вручную (вам просто не нужно это видеть или делать).Самое большое, что я ненавижу в Perl, - это отсутствие необязательных сигнатур методов, и это одна из самых привлекательных функций в Perl 6, а также в Moose.

14
ответ дан 30 November 2019 в 07:44
поделиться

Params :: Validate отлично работает, но, конечно, проверка аргументов замедляет работу. Тесты обязательны (по крайней мере, в коде, который я пишу).

1
ответ дан 30 November 2019 в 07:44
поделиться

Я хочу упомянуть здесь два момента. Первый - это тесты, второй - вопрос производительности.

1) Тесты

Вы упомянули, что тесты могут многое и что тесты - единственный способ убедиться, что ваш код правильный. В целом я бы сказал, что это абсолютно правильно. Но сами тесты решают только одну проблему.

Если вы пишете модуль, у вас есть две проблемы или, скажем, два разных человека, которые используют ваш модуль.

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

На будущее у меня есть один пример. Я написал модуль, используя Moose и некоторые другие вещи, мой код всегда заканчивался ошибкой сегментации. Затем я начал отлаживать свой код и искать проблему. Я трачу около 4 часов на поиск ошибки. В конце концов проблема заключалась в том, что я использовал Moose с чертой Array. Я использовал функцию "map", и я не предоставил функцию подпрограммы, просто строку или что-то еще.

Конечно, это была моя абсолютно глупая ошибка, но я трачу много времени на ее отладку. В конце концов, простая проверка того, что аргумент является подссылкой, будет стоить разработчику 10 секунд времени и будет стоить мне и, возможно, еще много времени.

Я знаю и другие примеры. Я написал клиент REST для интерфейса полностью ООП с Moose.В конце концов, вы всегда получаете объекты, вы можете изменять атрибуты, но убедитесь, что он не вызывает REST API для каждого изменения, которое вы сделали. Вместо этого вы меняете свои значения и, в конце концов, вызываете метод update (), который передает данные и изменяет значения.

Теперь у меня был пользователь, который тогда написал:

$obj->update({ foo => 'bar' })

Конечно, я получил обратно ошибку, update () не работает. Но убедитесь, что это не сработало , потому что метод update () не принимает хэш-ссылку. Он выполняет только синхронизацию фактического состояния объекта с онлайн-службой . Правильный код будет.

$obj->foo('bar');
$obj->update();

Первое работает, потому что я никогда не проверял аргументы. И я не выдаю ошибку, если кто-то приводит больше аргументов, чем я ожидал. Метод просто запускается нормально вроде.

sub update {
  my ( $self ) = @_;
  ...
}

Конечно, все мои тесты работают на 100% нормально. Но обработка этих ошибок, которые не являются ошибками, тоже требует времени. И это, вероятно, отнимает у пользователя много времени.

Итак, в конце. Да, тесты - это единственный правильный способ убедиться, что ваш код работает правильно. Но это не значит, что проверка типов бессмысленна. Проверка типов предназначена для того, чтобы помочь всем, не являющимся разработчиками (в вашем модуле) правильно использовать ваш модуль. И сэкономит вам и другим время при поиске ошибок дампа.

2) Производительность

Вкратце: вы не заботитесь о производительности, пока не позаботитесь.

Это означает, что пока ваш модуль не будет работать слишком медленно, производительность всегда будет достаточно высокой , и вам не нужно об этом заботиться. Если ваш модуль действительно работает , чтобы замедлить, вам необходимы дальнейшие исследования. Но для этих исследований вам следует использовать профилировщик, например Devel :: NYTProf, чтобы посмотреть, что работает медленно.

И я бы сказал.На 99% медлительность связана не с проверкой типа , а с вашим алгоритмом. Вы выполняете много вычислений, часто вызываете функции и т. Д. Часто помогает, если вы выполняете совершенно другие решения, используя другой лучший алгоритм, выполняя кеширование или что-то еще, а снижение производительности - это не ваша проверка типа. Но даже если проверка - это удар по производительности. Затем просто удалите его там, где это необходимо.

Нет причин оставлять проверку типов там, где производительность не имеет значение. Как вы думаете, проверка типов имеет значение в случае, подобном описанному выше? Где я написал клиент REST? 99% проблем с производительностью здесь - это количество запросов, поступающих к веб-сервису, или время для такого запроса. Отказ от проверки типов или MooseX :: Declare и т. Д., Вероятно, абсолютно ничего не ускорит.

И даже если вы видите недостатки в производительности. Иногда это приемлемо. Потому что скорость не имеет значения или иногда что-то дает вам большее значение . DBIx :: Class медленнее, чем чистый SQL с DBI, но DBIx :: Class дает вам много для этого.

2
ответ дан 30 November 2019 в 07:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: