Различают исключение и сбой в блоке CATCH [RAKU]

9

Мы знаем, что Отказ может быть обработан блоком CATCH.

В следующем примере мы создаем отказ AdHoc (в другом подпрограмме) и обрабатываем исключение в блоке CATCH (в моем подпрограмме)

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

Вывод следующий:

AdHoc Exception handled here
This was a Failure

Мой вопрос, однако: как мы можем различить Отказ и «нормальное» Исключение в блоке CATCH, чтобы различать два случая?

Джакар
источник

Ответы:

12

Отношение между Failureи Exceptionзаключается в том, что a Failureимеет Exception- то есть оно содержит объект исключения как часть своего состояния. Что-то вроде этого:

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

Когда Failure«взрывается», он делает это, бросая то, Exceptionчто находится внутри него. Таким образом, то, что достигает CATCHблока, является Exceptionобъектом, и нет обратной ссылки на вложение Failure. (На самом деле, данный Exceptionобъект в принципе может удерживаться многими Failureс.)

Следовательно, нет прямого способа обнаружить это. С точки зрения дизайна, вы, вероятно, не должны, и должны найти другой способ решения вашей проблемы. A Failure- это просто способ отложить выдачу исключения и позволить ему рассматриваться как значение; не предполагается, что природа основной проблемы меняется, потому что она передается как значение, а не как непосредственная передача потока управления. К сожалению, первоначальная цель не была указана в вопросе; вам может быть полезно взглянуть на исключения управления, но в противном случае, возможно, опубликуйте другой вопрос об основной проблеме, которую вы пытаетесь решить. Там, наверное, лучший способ.

Для полноты картины отмечу , что есть косвенные способы , которые можно обнаружить , что Exceptionбыл брошенной Failure. Например, если вы получили .backtraceобъект исключения и посмотрите на пакет верхнего фрейма, можно определить, что он исходит из Failure:

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

Однако это сильно зависит от деталей реализации, которые могут легко измениться, поэтому я бы не стал на это полагаться.

Джонатан Уортингтон
источник
Просто чтобы прояснить ситуацию, я собираюсь обработать только исключения (в блоке CATCH). В случае сбоя я хочу возобновить, как будто ничего не произошло, и позволить остальной части кода (за пределами CATCH) обработать сбой. В моем примере я не ожидал, что возвращенный сбой вызовет исключение! Все, что я сделал, это получил результат в $ b и проверил его как Bool. Это, на мой взгляд, не означает «использование» сбоя и, следовательно, запуск блока CATCH! Вместо этого кажется, что CATCH всегда обрабатывает исключение, содержащееся в ошибке !!
Джакар
Кроме того, в вашем примере, о косвенном способе обнаружения сбоя, возвращаемое значение Bool (из умной проверки типа сбоя) имеет значение «False». Но я ожидал, что это будет «Правда»! Я что-то пропустил???
Джакар
1
@jakar tryБлок подразумевает use fatalпрагму, что означает, что любой Failureвозвращенный вызов, сделанный в блоке, немедленно преобразуется в исключение. Просто не используйте try; А CATCHможет пойти в любой блок в Раку (так что просто сделайте это на уровне sub). В качестве альтернативы, напишите no fatalв верхней части вашего tryблока.
Джонатан Уортингтон
А как насчет моего второго комментария?
Джакар
1
Выполняя пример, который я дал, напечатал Trueна версии Rakudo, которую я имею на месте. Если это не по вашему, это просто доказывает, насколько хрупко это делать.
Джонатан Уортингтон
6

Просто удалите tryобертку:

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

Вы использовали try. A tryделает несколько вещей, но здесь уместно то, что он говорит Raku немедленно повышать любые Failures в своей области до исключений - это то, что вы говорите, что не хотите. Поэтому самое простое решение - просто перестать это делать.


Этот ответ лишь многословно повторяет часть объяснения jnthn (см., В частности, комментарии, которые он написал ниже своего ответа). Но я не был уверен, что все читатели заметят / поймут этот аспект, и не думал, что один или два комментария к ответу jnthn помогут, следовательно, этот ответ.

Я написал это как ответ сообщества, чтобы гарантировать, что я не получу никакой пользы от голосов, потому что это, очевидно, не гарантирует этого. Если он получит достаточно голосов, мы просто удалим его.

raiph
источник