Bedakan antara Pengecualian dan Kegagalan dalam blok CATCH [RAKU]

9

Kita tahu bahwa Kegagalan dapat ditangani oleh blok CATCH.

Pada contoh berikut ini, kami membuat Kegagalan 'AdHoc' (di sub-sub lainnya) dan kami menangani Pengecualian dalam blok CATCH (di sub-saya)

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();

Outputnya adalah sebagai berikut:

AdHoc Exception handled here
This was a Failure

Pertanyaan saya adalah: Bagaimana kita bisa membedakan antara Kegagalan dan pengecualian "normal" di blok CATCH untuk membedakan antara dua kasus?

jakar
sumber

Jawaban:

12

Hubungan antara Failuredan Exceptionadalah bahwa a Failurememiliki Exception- yaitu, ia memegang objek pengecualian sebagai bagian dari kondisinya. Sesuatu seperti ini:

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

Ketika Failure"meledak", ia melakukannya dengan melemparkan Exceptionyang ada di dalamnya. Jadi, yang mencapai CATCHblok adalah Exceptionobjek, dan tidak ada tautan kembali ke lampiran Failure. (Pada kenyataannya, Exceptionobjek yang diberikan pada prinsipnya dapat dipegang oleh banyak orang Failure.)

Karena itu, tidak ada cara langsung untuk mendeteksi ini. Dari perspektif desain, Anda mungkin seharusnya tidak, dan harus menemukan cara berbeda untuk menyelesaikan masalah Anda. A Failurehanyalah cara untuk menunda melempar pengecualian dan membiarkannya diperlakukan sebagai nilai; itu tidak dimaksudkan bahwa sifat dari masalah yang mendasarinya berubah karena itu disampaikan sebagai nilai daripada sebagai transfer langsung dari aliran kontrol. Sayangnya, tujuan awal tidak disebutkan dalam pertanyaan; Anda mungkin menemukan itu berguna untuk melihat pengecualian kontrol, tetapi sebaliknya mengirim pertanyaan lain tentang masalah mendasar yang Anda coba selesaikan. Mungkin ada cara yang lebih baik.

Untuk kelengkapan, saya akan perhatikan bahwa ada yang cara tidak langsung yang satu dapat mendeteksi bahwa Exceptiondilemparkan oleh Failure. Misalnya, jika Anda mendapatkan .backtraceobjek pengecualian dan melihat paket frame atas, dimungkinkan untuk menentukan bahwa itu berasal dari Failure:

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

Namun, ini sangat tergantung pada detail implementasi yang dapat dengan mudah diubah, jadi saya tidak akan bergantung padanya.

Jonathan Worthington
sumber
Hanya untuk mengklarifikasi semuanya, kehebatan saya adalah untuk menangani hanya untuk pengecualian (di blok CATCH). Dalam kasus Kegagalan saya ingin melanjutkan seolah-olah tidak ada yang terjadi dan biarkan sisa kode (di luar CATCH) menangani Kegagalan. Dalam contoh saya, saya tidak mengharapkan Kegagalan yang dikembalikan untuk memicu Pengecualian yang terkandung! Yang saya lakukan adalah mendapatkan hasilnya dalam $ b dan memeriksanya sebagai Bool. Dalam pandangan saya, itu bukan merupakan "penggunaan" Kegagalan dan karenanya memicu blok CATCH! Alih-alih itu tampaknya bahwa CATCH selalu menangani Pengecualian yang terkandung dalam Kegagalan !!
jakar
Lebih jauh dalam contoh Anda, tentang cara tidak langsung mendeteksi Kegagalan, Bool yang dikembalikan (dari pengecekan cerdas Tipe Kegagalan) bernilai "Salah". Tapi saya berharap itu "Benar"! Apakah saya melewatkan sesuatu ???
jakar
1
@ jakar tryBlok menyiratkan use fatalpragma, yang berarti setiap Failurepanggilan balik yang dilakukan dalam blok segera dikonversi menjadi pengecualian. Hanya saja jangan gunakan try; a CATCHbisa masuk di blok mana pun di Raku (jadi miliki saja di tingkat sub). Atau, tulis no fataldi bagian atas tryblok Anda .
Jonathan Worthington
Dan bagaimana dengan komentar kedua saya?
jakar
1
Menjalankan contoh yang saya berikan cetakan Truepada versi Rakudo yang saya miliki secara lokal. Jika tidak ada pada Anda, itu hanya membuktikan titik tentang kerapuhan melakukan ini.
Jonathan Worthington
6

Hapus trybungkusnya:

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();

Kamu dulu try. A trymelakukan beberapa hal, tetapi hal yang penting di sini adalah bahwa ia memberitahu Raku untuk segera mempromosikan apapun Failuredalam ruang lingkupnya ke pengecualian - yang menurut Anda tidak Anda inginkan. Jadi solusi paling sederhana adalah berhenti melakukan itu.


Jawaban ini hanya mengulangi sebagian dari penjelasan jnthn (lihat komentar khusus yang ditulisnya di bawah jawabannya). Tapi saya tidak yakin semua pembaca akan melihat / memahami aspek ini, dan tidak berpikir satu atau dua komentar pada jawaban jnthn akan membantu, maka jawaban ini.

Saya telah menulis ini sebagai jawaban komunitas untuk memastikan saya tidak akan mendapat manfaat dari upvotes karena itu jelas tidak menjamin itu. Jika cukup downvotes kami hanya akan menghapusnya.

raiph
sumber