Distinguere tra eccezione e errore in un blocco CATCH [RAKU]


9

Sappiamo che un errore può essere gestito da un blocco CATCH.

Nel seguente esempio creiamo un errore "AdHoc" (in other-sub) e gestiamo l'eccezione in un blocco CATCH (in my-sub)

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

L'output è il seguente:

AdHoc Exception handled here
This was a Failure

La mia domanda è però: come possiamo distinguere tra Failure e un'eccezione "normale" nel blocco CATCH per distinguere tra i due casi?

Risposte:


12

La relazione tra Failuree Exceptionè che a Failureha unException - vale a dire, contiene l'oggetto eccezione come parte del suo stato. Qualcosa come questo:

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

Quando un Failure"esplode", lo fa lanciando Exceptionciò che è al suo interno. Pertanto, ciò che raggiunge il CATCHblocco è l' Exceptionoggetto, e non vi è alcun collegamento con il recinto Failure. (In effetti, un datoException oggetto potrebbe in linea di principio essere trattenuto da molti Failures.)

Pertanto, non esiste un modo diretto per rilevare questo. Dal punto di vista del design, probabilmente non dovresti esserlo e dovresti trovare un modo diverso di risolvere il tuo problema. UNFailure è solo un modo per rinviare il lancio di un'eccezione e consentire che venga trattato come un valore; non si intende che la natura del problema sottostante cambi perché viene trasmessa come valore anziché come trasferimento immediato del flusso di controllo. Sfortunatamente, l'obiettivo iniziale non era indicato nella domanda; potresti trovare utile esaminare le eccezioni di controllo, ma in caso contrario potresti pubblicare un'altra domanda sul problema di fondo che stai cercando di risolvere. Probabilmente c'è un modo migliore.

Per completezza, noterò che ci sono modi indiretti in cui si può rilevare che il è Exceptionstato lanciato da a Failure. Ad esempio, se si ottiene l' .backtraceoggetto dell'eccezione e si osserva il pacchetto del frame superiore, è possibile determinare che proviene da Failure:

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

Tuttavia, questo dipende fortemente dai dettagli di implementazione che potrebbero facilmente cambiare, quindi non mi affiderei.


Solo per chiarire le cose, la mia intenzione è quella di gestire solo le eccezioni (nel blocco CATCH). In caso di guasto, voglio riprendere come se nulla fosse e lasciare che il resto del codice (al di fuori di CATCH) gestisca il guasto. Nel mio esempio non mi aspettavo che l'errore restituito innescasse l'eccezione contenuta! Tutto quello che ho fatto è ottenere il risultato in $ b e controllarlo come Bool. Ciò, a mio avviso, non costituisce un "uso" del fallimento e quindi l'attivazione del blocco CATCH! Invece, sembra che CATCH gestisca sempre l'eccezione contenuta nell'errore !!
Jakar il

Inoltre, nel tuo esempio, riguardo al modo indiretto di rilevare un errore, il valore restituito (dal controllo intelligente del tipo di errore) ha valore "Falso". Ma mi aspettavo che fosse "Vero"! Ho dimenticato qualcosa???
Jakar il

1
@jakar Un tryblocco implica il use fatalpragma, il che significa che qualsiasi Failureritorno da una chiamata effettuata nel blocco viene immediatamente convertito in un'eccezione. Basta non usare try; a CATCHpuò andare in qualsiasi blocco in Raku (quindi basta averlo a livello di sub). In alternativa, scrivi no fatalnella parte superiore del tryblocco.
Jonathan Worthington,

E il mio secondo commento?
Jakar

1
TrueSeguendo l'esempio che ho dato stampe sulla versione Rakudo che ho localmente. Se non lo fa sul tuo, questo dimostra solo il punto sulla fragilità di farlo.
Jonathan Worthington

6

Basta rimuovere il trywrapper:

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

Hai usato try. A tryfa alcune cose, ma la cosa pertinente qui è che dice a Raku di promuovere immediatamente qualsiasi Failures nel suo ambito di eccezioni - che è ciò che dici di non voler. Quindi la soluzione più semplice è semplicemente smettere di farlo.


Questa risposta ripete solo verbalmente parte della spiegazione di jnthn (vedi in particolare i commenti che ha scritto sotto la sua risposta). Ma non ero convinto che tutti i lettori avrebbero individuato / compreso questo aspetto, e non pensavo che un commento o due sulla risposta di jnthn avrebbero aiutato, quindi questa risposta.

Ho scritto questo come una risposta della comunità per assicurarmi che non trarrò beneficio da alcun voto perché ovviamente non lo garantisce. Se ottiene abbastanza downvotes lo elimineremo semplicemente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.