Phân biệt giữa Ngoại lệ và Thất bại trong khối CATCH [RAKU]


9

Chúng tôi biết rằng Thất bại có thể được xử lý bởi khối CATCH.

Trong ví dụ sau, chúng tôi tạo ra Lỗi 'AdHoc' (trong phần phụ khác) và chúng tôi xử lý Ngoại lệ trong khối CATCH (trong phần phụ của tôi)

sub my-sub {
    try {
        CATCH {
            when X::AdHoc { say 'AdHoc Exception handled here'; .resume }
            default {say 'Other Exception'; .resume}
        }

        my $b = other-sub();

        $b.so ?? $b.say !! 'This was a Failure'.say;
    }
}

sub other-sub { fail 'Failure_X' }

my-sub();

Đầu ra là như sau:

AdHoc Exception handled here
This was a Failure

Câu hỏi của tôi là: Làm thế nào chúng ta có thể phân biệt giữa Thất bại và Ngoại lệ "bình thường" trong khối CATCH để phân biệt giữa hai trường hợp?

Câu trả lời:


12

Mối quan hệ giữa FailureExceptionFailurecó một Exception- có nghĩa là, nó giữ đối tượng ngoại lệ như là một phần của trạng thái của nó. Một cái gì đó như thế này:

class Failure {
    has Exception $.exception;
    # ...
}

Khi một Failure"phát nổ", nó làm như vậy bằng cách ném cái Exceptionbên trong nó. Do đó, những gì đạt đến CATCHkhối là Exceptionđối tượng và không có liên kết trở lại kèm theo Failure. (Trên thực tế, một Exceptionđối tượng nhất định về nguyên tắc có thể được giữ bởi nhiều Failures.)

Do đó, không có cách nào trực tiếp để phát hiện điều này. Từ quan điểm thiết kế, có lẽ bạn không nên và nên tìm một cách khác để giải quyết vấn đề của mình. A Failurechỉ là một cách để trì hoãn việc ném một ngoại lệ và cho phép nó được coi là một giá trị; nó không có ý định rằng bản chất của vấn đề tiềm ẩn thay đổi bởi vì nó được truyền đạt như một giá trị chứ không phải là sự chuyển giao ngay lập tức của dòng kiểm soát. Thật không may, mục tiêu ban đầu không được nêu trong câu hỏi; bạn có thể thấy hữu ích khi xem xét các ngoại lệ kiểm soát, nhưng nếu không thì có thể đăng một câu hỏi khác về vấn đề tiềm ẩn mà bạn đang cố gắng giải quyết. Có lẽ có một cách tốt hơn.

Để đầy đủ, tôi sẽ lưu ý rằng có những cách gián tiếp mà người ta có thể phát hiện ra rằng nó Exceptionđã bị ném bởi a Failure. Ví dụ: nếu bạn có được .backtraceđối tượng ngoại lệ và nhìn vào gói của khung trên cùng, có thể xác định rằng nó đến từ Failure:

sub foo() { fail X::AdHoc.new(message => "foo") }
try {
    foo();
    CATCH {
        note do { no fatal; .backtrace[0].code.package ~~ Failure };
        .resume
    }
}

Tuy nhiên, điều này phụ thuộc rất nhiều vào các chi tiết triển khai có thể dễ dàng thay đổi, vì vậy tôi không dựa vào nó.


Chỉ cần làm rõ mọi chuyện, ý định của tôi là chỉ xử lý các trường hợp ngoại lệ (trong khối CATCH). Trong trường hợp Thất bại, tôi muốn tiếp tục như thể không có gì xảy ra và để phần còn lại của mã (bên ngoài CATCH) xử lý Lỗi. Trong ví dụ của tôi, tôi không mong đợi Thất bại được trả về sẽ kích hoạt Ngoại lệ được chứa! Tất cả những gì tôi đã làm là nhận kết quả bằng $ b và kiểm tra nó dưới dạng Bool. Điều đó, theo quan điểm của tôi, không cấu thành "sử dụng" Thất bại và do đó kích hoạt khối CATCH! Thay vào đó, có vẻ như CATCH luôn xử lý Ngoại lệ có trong Thất bại !!
Jakar

Hơn nữa, trong ví dụ của bạn, về cách gián tiếp phát hiện Lỗi, Bool được trả về (từ kiểm tra thông minh Loại Thất bại) có giá trị "Sai". Nhưng tôi mong đợi nó là "Đúng"! Tôi có bỏ lỡ điều gì không ???
jakar

1
@jakar Một trykhối ngụ ý use fatalpragma, có nghĩa là bất kỳ Failuretrả lại từ một cuộc gọi được thực hiện trong khối ngay lập tức được chuyển đổi thành một ngoại lệ. Chỉ không sử dụng try; a CATCHcó thể đi trong bất kỳ khối nào trong Raku (vì vậy chỉ cần có nó ở cấp độ của sub). Ngoài ra, viết no fatalở đầu trykhối của bạn .
Jonathan Worthington

Và những gì về nhận xét thứ hai của tôi?
jakar

1
Chạy ví dụ tôi đã in Truetrên phiên bản Rakudo mà tôi có tại địa phương. Nếu nó không thuộc về bạn, điều đó chỉ chứng tỏ quan điểm về sự mong manh của việc này.
Jonathan Worthington

6

Chỉ cần loại bỏ trytrình bao bọc:

sub my-sub {

#    try {              <--- remove this line...

        CATCH {
            when X::AdHoc { say 'AdHoc Exception handled here'; .resume }
            default {say 'Other Exception'; .resume}
        }

        my $b = other-sub();

        $b.so ?? $b.say !! 'This was a Failure'.say;

#    }                  <--- ...and this one

}

sub other-sub { fail 'Failure_X' }

my-sub();

Bạn đã dùng try . A trylàm một vài điều, nhưng điều thích hợp ở đây là nó bảo Raku ngay lập tức thúc đẩy bất kỳ Failures nào trong phạm vi của nó đến các ngoại lệ - đó là những gì bạn nói bạn không muốn. Vì vậy, giải pháp đơn giản nhất là chỉ cần ngừng làm điều đó.


Câu trả lời này chỉ lặp lại một cách rõ ràng một phần lời giải thích của jnthn (xem những bình luận cụ thể mà anh ấy đã viết bên dưới câu trả lời của mình). Nhưng tôi đã không tin rằng tất cả các độc giả sẽ phát hiện / hiểu khía cạnh này và không nghĩ rằng một hoặc hai nhận xét về câu trả lời của jnthn sẽ có ích, vì vậy câu trả lời này.

Tôi đã viết điều này như một câu trả lời của cộng đồng để đảm bảo tôi sẽ không được hưởng lợi từ bất kỳ sự ủng hộ nào vì rõ ràng nó không đảm bảo điều đó. Nếu nó đủ tải xuống, chúng tôi sẽ xóa nó.

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.