Try :: Tiny có còn được khuyến nghị để xử lý ngoại lệ trong Perl 5.14 trở lên không?


76

Sự đồng thuận của cộng đồng Perl dường như Try::Tinylà cách ưa thích để xử lý các trường hợp ngoại lệ.

Perl 5.14 (là phiên bản tôi sử dụng) dường như giải quyết được các vấn đề với evalcác Try::Tinyđịa chỉ đó . Try::TinyVẫn sẽ cung cấp bất kỳ lợi ích cho tôi?


6
Tôi cũng quan tâm đến câu trả lời của cộng đồng về vấn đề này! Câu hỏi hay!
Joel Berger

Câu trả lời:


34

Câu trả lời của tôi là không phổ biến, nhưng tôi không nghĩ rằng các lập trình viên Perl nên cố gắng sử dụng khái niệm cực kỳ tồi tệ về thứ mà chúng ta gọi là "ngoại lệ" trong Perl. Đây thực chất là giá trị trả về kênh phụ. Tuy nhiên, vẫn còn say mê với ý tưởng về các ngoại lệ, ngay cả với tất cả sự phức tạp của việc sử dụng một biến toàn cục để chuyển xung quanh trạng thái, mọi người vẫn tiếp tục cố gắng làm cho nó hoạt động.

Tuy nhiên, trên thực tế, mọi người sử dụng dieđể báo hiệu sự thất bại. Một số người sẽ nói rằng bạn có thể dievới một tham chiếu và trả lại các đối tượng lỗi, nhưng bạn không cần dieđiều đó. Chúng ta có các đối tượng, vì vậy chúng ta nên sử dụng tất cả sức mạnh của các đối tượng:

 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 ) { ... };

Điều đó không liên quan đến các biến toàn cục, hành động ở khoảng cách xa, đau đầu trong phạm vi hoặc yêu cầu đặc biệt đặc biệt. Bạn tạo một lớp nhỏ Result, hoặc bất cứ thứ gì bạn muốn gọi nó, để bọc các giá trị trả về của bạn để bạn có dữ liệu có cấu trúc thay vì các giá trị đơn lẻ không có danh tính. Không còn thắc mắc giá trị trả về có nghĩa là gì. Đó là undefgiá trị thực hay là dấu hiệu của sự thất bại? Giá trị trả về có tốt không nếu nó được xác định hoặc nếu nó đúng? Đối tượng của bạn có thể cho bạn biết những điều này. Và, bạn có thể sử dụng cùng một đối tượng vớidie . Nếu bạn đã sử dụng đối tượng với dievà sử dụng nó làm giá trị trả về, thì có rất ít điều để giới thiệu tất cả những thứ bổ sung mà bạn phải làm để dung nạp$@ .

Tôi nói thêm về điều này trong "Trả lại các đối tượng lỗi thay vì ném các ngoại lệ"

Tuy nhiên, tôi biết rằng bạn không thể giúp những gì người khác làm, vì vậy bạn vẫn phải giả vờ Perl có ngoại lệ.


2
Tôi đồng ý, nhưng như bạn đã nói ở phần cuối, có những mô-đun mà diekhi chúng không nên, vì vậy chúng ta vẫn cần biết cơ chế nào để bẫy những ngoại lệ đó. Các nhà thiết kế mô-đun trong tương lai, hãy xem xét cách tiếp cận này!
Joel Berger

6
Giải pháp rất tốt, nhưng die / exception có một lợi thế lớn: lan truyền thông qua ngăn xếp các cuộc gọi phụ. Ý tôi là: không kiểm tra việc đạt hay không đạt của một số lệnh gọi chương trình con - chỉ cần không bắt một ngoại lệ. Nó sẽ lan rộng, cho đến khi ai đó bắt được nó.
msztolcman

2
Công cụ truyền bá đó là một cách rất kém để thiết kế một chương trình. Những cấp cao hơn phải làm gì để xử lý lỗi? Trong Perl, bạn không thể xử lý nó và tiếp tục nơi bạn đã dừng lại, vì vậy bạn không thực sự xử lý được nó. Phương pháp tôi chỉ ra cũng có thể phổ biến. Mỗi cấp độ có thể thêm vào kết quả mà nó nhận được và vượt qua.
brian d foy

8
Rất nghèo? Tôi không nghĩ vậy. Các lỗi nên được xử lý ở đó, nơi chúng ta có thể nói phải làm gì với chúng. Nhiều lỗi không nên được xử lý - chỉ cần ghi nhật ký và hiển thị một số thông báo cho người dùng (điều này phải nằm trong GUI, không phải trong các chức năng cấp thấp nhất). Nó chỉ là giả định về thiết kế và có một số ưu và nhược điểm (như mọi khi).
msztolcman

6
Tôi có thể hiểu tại sao bạn đưa ra lập luận của mình, nhưng nó yêu cầu một lập trình viên không bao giờ quên kiểm tra lỗi. Chúng tôi biết họ nên làm, nhưng những gì chúng tôi nên làm trên lý thuyết và những gì chúng tôi làm trong thực tế khi bị hạn chế bởi thời hạn không giống nhau. Do đó, chúng tôi có thể quên kiểm tra một lỗi nghiêm trọng đó và để mã của chúng tôi tiếp tục hoạt động, hoàn toàn không biết rằng nó đang làm hỏng dữ liệu ở mọi nơi.
Rộng rãi

31

Nó luôn luôn là một trường hợp sở thích cá nhân. Bạn có thích

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

hoặc là

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

Nhưng hãy nhớ rằng cái sau sử dụng anon subs, vì vậy nó sẽ gây rối return, cũng nhưnext và tương tự. Hãy thử :: Quá trình thử bắt của Tiny có thể sẽ phức tạp hơn nhiều khi bạn thêm các kênh giao tiếp giữa khối bắt và bên ngoài khối đó.

Trường hợp tốt nhất (đơn giản nhất) để trả về ngoại lệ là nếu $rvluôn đúng khi không có ngoại lệ. Nó sẽ giống như sau:

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

vs

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

if (!$rv) {
   return;
}

Đó là lý do tại sao tôi sẽ sử dụng TryCatch thay vì Try :: Tiny là tôi sử dụng một mô-đun như vậy.

Thay đổi thành Perl chỉ đơn giản là bạn có thể làm if ($@)lại. Nói cách khác,

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

có thể được viết

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

4
Nếu bạn không cần bắt giá trị được trả về bởi eval (đối với tôi ít nhất là trường hợp phổ biến nhất) - thì phiên bản eval trở nên đơn giản hơn một chút.
zby

TryCatch có ít cảnh báo hơn so với Try :: Tiny, và nó nhanh hơn nhiều. Có một cảnh báo, nó tải chậm kinh khủng.
Schwern

@Schwern, Một giải pháp dựa trên Devel :: CallParser sẽ tải nhanh hơn.
ikegami

@ikegami Không, vấn đề là nó phụ thuộc vào Moose và Parse :: Method :: Signatures (phần ruột của MooseX :: Method :: Signatures).
Schwern

@Schwen, Đó là lý do tại sao Devel :: CallParser sẽ giúp bạn. Nó sẽ cho phép bạn sử dụng perlcác chức năng phân tích cú pháp nội trang.
ikegami

14

Nếu không có gì khác, Try::Tinyvẫn là đường cú pháp đẹp. Nếu bạn muốn thứ gì đó nặng hơn một chút, thì cũng có TryCatch, giải pháp giải quyết một số vấn đề liên quan đến thực tế là các mệnh đề trong Try::Tinylà chương trình con (ví dụ: returnkhông rời khỏi hàm bao quanh).


11

Try::Tinydễ dàng và nhẹ. Quá dễ dàng. Chúng tôi gặp hai vấn đề:

  • người đăng ký ẩn danh - luôn có vấn đề với 'return câu lệnh '' bên trong
  • bắt luôn và mọi thứ

Vì vậy, tôi đã thực hiện một số thay đổi để Try::Tinygiúp chúng tôi. Bây giờ chúng tôi có:

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

Tôi biết - cú pháp này hơi kỳ lạ, nhưng nhờ có bằng chứng ' sub', các lập trình viên của chúng tôi giờ đây biết rằng returncâu lệnh '' chỉ thoát khỏi trình xử lý ngoại lệ và chúng tôi luôn chỉ bắt các ngoại lệ này mà chúng tôi muốn bắt :)


1
Lưu ý rằng bạn có thể đã sử dụng sub{}mà không có bất kỳ thay đổi nào đối với Try :: Tiny:perl -MTry::Tiny -E"&try(sub { say 'A'; die qq{B\n} if $ARGV[0] }, &catch(sub { print; }));" 1
ikegami

2
Nhưng quan trọng hơn, hãy lưu ý rằng các mô-đun khác (ví dụ: TryCatch ) sử dụng các khối thực, không phải con anon, để tránh tiếng ồn.
ikegami

1
Không có cách nào để không bắt tất cả mọi thứ trong Perl, trừ khi bạn viết mã mà lại ném ngoại lệ không được công nhận :)
Hobbs

2
@hobbs: tất nhiên :) nhưng nếu rethrow được thực hiện mà không có sự tham gia của bạn ... thì tốt hơn nhiều;) Điều quan trọng nhất ở đây là từ khóa khác nhau để bắt tất cả các trường hợp ngoại lệ sau đó chỉ bắt một vài trường hợp trong số đó :)
msztolcman

3
Phụ thuộc vào những gì bạn có ý nghĩa của ánh sáng. Về CPU, Try :: Tiny thua nặng. Mặt khác, TryCatch có nhiều phụ thuộc phức tạp hơn.
ikegami

0

Hoặc làm:

local $@;
eval { … }

… Để ngăn các thay đổi đối với $ @ ảnh hưởng đến phạm vi toàn cầu hoặc sử dụng Try :: Tiny.

Về mặt cú pháp, có những tình huống mà tôi thích cái này hay cái kia.


0

Try :: Tiny rất tuyệt, nhưng yêu cầu dấu chấm phẩy trên dấu ngoặc nhọn cuối cùng và không cho phép sử dụng phép gán biến ngoại lệ và chưa nói đến việc bắt lớp ngoại lệ. TryCatch đã từng làm rất tốt, nhưng đã bị phá vỡ với phiên bản mới 0.006020 của Devel :: Declare . Một triển khai tuyệt vời khác là Cú pháp :: Từ khóa :: Thử nhưng nó không triển khai các phép gán biến ngoại lệ hoặc bắt lớp ngoại lệ.

Có một mô-đun mới Nice :: Try , là một sự thay thế hoàn hảo.

Không cần phải có dấu chấm phẩy ở dấu ngoặc cuối như Try :: Tiny.

Bạn cũng có thể thực hiện gán biến ngoại lệ như

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

Nó cũng hoạt động bằng cách sử dụng ngoại lệ lớp như

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

Và nó cũng hỗ trợ finally. Bộ tính năng của nó làm cho nó khá độc đáo.

Tiết lộ đầy đủ: Tôi đã phát triển mô-đun này khi TryCatch bị hỏng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.