Стоит ли проверять аргументы функции Perl? - PullRequest
18 голосов
/ 25 февраля 2010

Есть много шума по поводу 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;, и мы быстро слышим об этом - утка, печатающая подход.

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

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

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

Ответы [ 9 ]

14 голосов
/ 25 февраля 2010

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

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

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

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

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

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

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

7 голосов
/ 25 февраля 2010

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

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

5 голосов
/ 15 декабря 2011

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

4 голосов
/ 25 февраля 2010

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

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

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

2 голосов
/ 12 мая 2010

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

1) Тесты

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1 голос
/ 10 марта 2010

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

0 голосов
/ 14 февраля 2013

Иногда. Я обычно делаю это всякий раз, когда я передаю параметры через хэш или 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 позволяет довольно легко выполнять проверку типов, проверку допустимых значений, значения по умолчанию, обязательные параметры, сохранение значений параметров в других переменных и многое другое.

0 голосов
/ 03 июля 2012

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

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

0 голосов
/ 16 декабря 2011

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

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

Используйте проверку типа. Защитное кодирование того стоит.

Patrick.

...