Подтвердить что ты не робот

Проверяет ли аргументы функции Perl?

Там много шума о MooseX::Method::Signatures и даже до этого, такие модули, как Params::Validate, которые предназначены для ввода каждого аргумента методам или функциям. Я рассматриваю возможность использования первого для всего моего будущего 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;, и мы быстро об этом узнаем - duck typing.

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

Теперь это звучит как глупость, но следует учитывать некоторые случаи, когда это не так. Мне действительно интересно, что в @_ здесь?

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

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

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

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

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

Ответ 3

Да, его стоит - защитное программирование - одна из тех вещей, которые всегда стоят того.

Ответ 4

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

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

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

Ответ 5

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

1) Тесты

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 6

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

Ответ 7

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

Удар производительности определенно существует, но его можно управлять. 2 часа использования NYTProf помогли мне найти несколько атрибутов Moose, которые я слишком сильно измельчал, и я просто переработал свой код и получил 4-кратное повышение производительности.

Использовать проверку типов. Оборонительное кодирование того стоит.

Патрик.

Ответ 8

Да, это стоит того, потому что это поможет во время разработки, обслуживания, отладки и т.д.

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

Ответ 9

Иногда. Я обычно делаю это, когда я передаю опции через хэш или hashref. В этих случаях очень легко забыть или пропустить опцию, и проверка с помощью Params::Check может сэкономить много времени для устранения неполадок.

Например:

sub revise {
    my ($file, $options) = @_;

    my $tmpl = {
        test_mode => { allow => [0,1], 'default' => 0 },
        verbosity => { allow => qw/^\d+$/, 'default' => 1 },
        force_update => { allow => [0,1], 'default' => 0 },
        required_fields => { 'default' => [] },
        create_backup => { allow => [0,1], 'default' => 1 },
    };

    my $args = check($tmpl, $options, 1)
      or croak "Could not parse arguments: " . Params::Check::last_error();
    ...
}

Прежде чем добавлять эти проверки, я бы забыл, используют ли имена подчеркивания или дефисы, передайте require_backup вместо create_backup и т.д. И это для кода, который я написал сам, - если другие люди будут использовать это, вы должны определенно сделать какую-то идиотскую проверку. Params::Check позволяет довольно легко выполнять проверку типов, проверку допустимых значений, значения по умолчанию, требуемые параметры, сохранение значений опций другим переменным и т.д.