Спроба :: Tiny все ще рекомендується для обробки винятків у Perl 5.14 або пізнішої версії?


76

Здається, що консенсус спільноти Perl Try::Tinyє найкращим способом обробки винятків.

Perl 5,14 (що є використанням версії I) , здається , щоб вирішити ті проблеми , з того, evalщо Try::Tinyадресою. Чи Try::Tinyнадаватимуть мені ще якісь переваги?


6
Мене цікавить відповідь громади і на це! Гарне питання!
Джоел Бергер,

Відповіді:


34

Моя відповідь непопулярна, але я не думаю, що програмісти Perl повинні намагатися використовувати надзвичайно погане уявлення про те, що ми називаємо "винятками" в Perl. Це, по суті, значення повернення бічного каналу. Однак, як і раніше захоплені ідеєю винятків, навіть незважаючи на всю складність використання глобальної змінної для обходу стану, люди продовжують намагатися змусити її працювати.

Практично, однак, люди використовують dieдля сигналізації про несправність. Деякі скажуть, що ви можете dieза допомогою посилання та повернути об'єкти помилок, але вам це не потрібно die. У нас є об’єкти, тому слід використовувати всю силу об’єктів:

 sub some_sub {
    ...
    return Result->new( error => 1, description => ... ) if $something_went_wrong;
    return Result->new( error => 0, ... );
    }

 my $result = some_sub( ... );
 if( $result->is_error ) { ... };

Це не включає глобальні змінні, дії на відстань, масштабні головні болі або вимагає спеціальних спеціальних пропозицій. Ви створюєте крихітний клас Resultабо, як ви хочете його назвати, щоб обернути свої повернені значення, щоб у вас були структуровані дані замість окремих значень без ідентичності. Більше не цікаво, що означає повернена вартість. Це undefсправжнє значення або ознака відмови? Чи добре значення повернення, якщо воно визначене або якщо воно відповідає дійсності? Ваш предмет може розповісти вам ці речі. І, ви можете використовувати той самий об’єкт з die. Якщо ви вже використовуєте об'єкт dieі використовуєте його як повернене значення, дуже мало рекомендувати всі зайві речі, які потрібно зробити, щоб терпіти $@.

Більше про це я розповідаю в "Повернути об'єкти помилок замість викидів"

Однак я знаю, що ви не можете допомогти тому, що роблять інші люди, тому вам все одно доведеться робити вигляд, що Perl має винятки.


2
Я згоден, але, як ви сказали наприкінці, є модулі, які dieколи цього не повинні, тож нам все одно потрібно знати, який механізм зафіксувати ці винятки. Майбутні дизайнери модулів, розгляньте цей підхід!
Джоел Бергер,

6
Дуже хороше рішення, але матриця / виняток має одну велику перевагу: поширення через стек допоміжних викликів. Я маю на увазі: не тестуйте на передачу чи провал якогось виклику підпрограми - просто не вловлюйте виняток. Він буде розмножуватися, поки хтось не зловить його.
msztolcman

2
Це розповсюдження - дуже поганий спосіб розробити програму. Що повинні робити ті вищі рівні, щоб усунути помилку? У Perl ви не можете впоратись із цим і продовжити там, де ви зупинилися, тому ви насправді зовсім не справляєтесь із цим. Метод, який я показую, може поширюватися так само добре. Кожен рівень може додати до результату, який він отримує та передає.
brian d foy

8
Дуже погане? Я не думаю. Помилки слід обробляти там, де ми можемо сказати, що з ними робити. Багато помилок не слід обробляти - просто увійти в систему та показати повідомлення користувачеві (це має бути в графічному інтерфейсі, а не в функціях найнижчого рівня). Це просто припущення щодо дизайну, і у нього є деякі плюси і мінуси (як завжди).
msztolcman

6
Я розумію, чому ви аргументуєте, але для цього потрібно, щоб програміст ніколи не забував перевірити наявність помилок. Ми знаємо, що вони повинні, але те, що ми маємо робити теоретично і що ми робимо на практиці, коли в умовах жорсткого терміну не одне і те ж. Таким чином, ми могли б забути , щоб перевірити , що однією критичною помилки і є наш код продовжувати, в щасливому невіданні , що він всюди розбещує свої дані.
Овідій,

31

Це завжди було випадком особистих переваг. Ти надаєш перевагу

my $rv;
if (!eval { $rv = f(); 1 } ) {
   ...
}

або

my $rv = try {
   f();
} catch {
   ...
};

Але майте на увазі, що останній використовує anon subs, тому він псується return, а також nextтощо. Спробуйте :: спроба-лову Tiny може виявитись набагато складнішою, оскільки ви додаєте канали зв'язку між блоком catch і поза ним.

Найкращий (найпростіший) сценарій повернення винятків - це якщо $rvзавжди є істинним, коли винятку немає. Це виглядало б так:

my $rv;
if ($rv = eval { f() }) {
   ...
   return;
}

проти

my $rv = try {
   f();
} catch {
   ...
};

if (!$rv) {
   return;
}

Тому я б використовував TryCatch замість Try :: Tiny, якби я використовував такий модуль.

Зміна Perl просто означає, що ви можете зробити це if ($@)знову. Іншими словами,

my $rv;
if (!eval { $rv = f(); 1 } ) {
   ...
}

можна написати

my $rv = eval { f() };
if ($@) {
   ...
}

4
Якщо вам не потрібно ловити значення, яке повертає eval (що принаймні для мене є найпоширенішим випадком) - тоді версія eval стає дещо простішою.
zby

TryCatch має набагато менше застережень, ніж Try :: Tiny, і це набагато швидше. Є одне застереження: завантажувати це мерзенно повільно.
Schwern

@Schwern, Рішення на основі Devel :: CallParser має завантажуватися швидше.
ikegami

@ikegami Ні, проблема полягає в тому, що вона покладається на Moose and Parse :: Method :: Signatures (кишки MooseX :: Method :: Signatures).
Schwern

@Schwen, Ось чому Devel :: CallParser допоможе. Це дозволить вам використовувати perlвбудовані функції синтаксичного аналізу.
ikegami

14

Якщо нічого іншого, Try::Tinyвсе одно приємний синтаксичний цукор. Якщо ви хочете щось більш важке, це також TryCatchвирішує деякі проблеми, пов’язані з тим, що пункти в Try::Tinyє підпрограмами (наприклад, returnце не залишає функції , що включає).


11

Try::Tinyлегкий і легкий. Надто легко. У нас були дві проблеми:

  • анонімні підписки - returnвсередині завжди були проблеми із заявою ' '
  • ловити завжди і все

Тож я вніс деякі зміни Try::Tiny, які нам допомагають. Тепер ми маємо:

try sub {},
catch 'SomeException' => sub {},
catch [qw/Exception1 Exception2/] => sub {},
catch_all sub {};

Я знаю - цей синтаксис трохи екзотичний, але завдяки очевидним ' sub', наші програмісти тепер знають, що returnоператор ' ' виходить просто з обробника винятків, і ми завжди ловимо лише ці винятки, які ми хочемо вловити :)


1
Зверніть увагу, що ви могли б sub{}без будь-яких змін використовувати Try :: Tiny:perl -MTry::Tiny -E"&try(sub { say 'A'; die qq{B\n} if $ARGV[0] }, &catch(sub { print; }));" 1
ikegami

2
Але що ще важливіше, зауважте, що інші модулі (наприклад, TryCatch ) використовують реальні блоки, а не анонси, уникаючи шуму.
ikegami

1
Немає можливості не вловити все в Perl, якщо ви не напишете код, який повторно викидає невизнані
помилки

2
@hobbs: звичайно :) але якщо повторний бросок здійснюється без вашої участі в ньому ... це набагато краще;) Найголовніше тут - інше ключове слово для лову всіх винятків, ніж для лову лише декількох з них :)
msztolcman

3
Залежить від того, що ви маєте на увазі під світлом. Що стосується центрального процесора, спробуйте :: Tiny сильно програє. З іншого боку, TryCatch має більш складні залежності.
ikegami

0

Або зробіть:

local $@;
eval { … }

... щоб запобігти змінам $ @ від впливу на глобальний обсяг, або використовуйте Try :: Tiny.

Синтаксично бувають ситуації, коли я віддаю перевагу тому чи іншому.


0

Try :: Tiny - це чудово, але для останньої фігурної дужки потрібна крапка з комою і не дозволяє використовувати присвоєння змінних винятків, не кажучи вже про лов класу винятків. TryCatch мав звичай робити велику роботу, але був розбитий з новою версією 0.006020 від Devel :: Declare . Ще однією чудовою реалізацією є Синтаксис :: Ключове слово :: Спробуйте, але він не реалізує призначення змінних винятків або логічний клас винятків.

З'явився новий модуль Nice :: Try , який є ідеальною заміною.

Немає необхідності мати крапку з комою на останній фігурній дужці, як Try :: Tiny.

Ви також можете зробити присвоєння змінної винятку, наприклад

  try
  {
    # something
  }
  catch( $e )
  {
    # catch this in $e
  }

Він також працює за допомогою винятків класів, таких як

  try
  {
    # something
  }
  catch( Exception $e )
  {
    # catch this in $e
  }

І це також підтримує finally. Набір функцій робить його досить унікальним.

Повна інформація: Я розробив цей модуль, коли TryCatch зламався.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.