1
votes

Ce bloc de code de la documentation Scala a-t-il un sens?

Cet exemple provient de https: // www .scala-lang.org / api / current / scala / util / control / ControlThrowable.html :

import scala.util.control.ControlThrowable

try {
  // Body might throw arbitrarily
} catch {
  case c: ControlThrowable => throw c // propagate
  case t: Exception        => log(t)  // log and suppress
}

Je comprends pourquoi Throwable ne doit pas être attrapé, mais dans cet exemple, nous pourrions supprimer case c: ControlThrowable => throw c // propager sans aucun problème car le cas suivant attrape Exception , donc ControlThrowable s ne sera de toute façon pas intercepté. Est-ce que je manque quelque chose ici?


3 commentaires

Attraper un jetable est considéré comme une mauvaise pratique dans Scala. ControlThrowable est destiné à être utilisé dans de rares cas où le programmeur a un Throwable qui est destiné au contrôle de flux. Voir ici


Comme je l'ai dit, je comprends qu'attraper Throwable est une mauvaise pratique. Cela est expliqué dans le lien que j'ai envoyé aussi. Mais la suppression de cette ligne ne signifie pas que tous les Throwable seront interceptés - Exception est un sous-type de Throwable .


@jrook ControlThrowable est utilisé par Scala en interne. Il n'est presque jamais utilisé par le programmeur.


3 Réponses :


3
votes

Ce n'est pas inutile.

Considérez ceci:

class FooException extends Exception with ControlThrowable

Cela serait intercepté par le premier bloc et renvoyé, sans jamais atteindre le deuxième bloc. C'est probablement une mauvaise idée d'écrire des exceptions comme ça, mais, lorsque ControlThrowable a été introduit pour la première fois (alors appelé ControlException), c'était n'est pas rare dans la bibliothèque standard .


1 commentaires

Dois-je donc m'attendre à des exceptions qui sont également ControlThrowable dans la version 2.11+ ou est-ce une chose du passé?



0
votes

À l'origine, le code est écrit sous la forme Commentaire sur le code source de ControlThrowable .

Comme vous l'avez mentionné, vous pouvez supprimer la ligne sauf si vous avez la possibilité de produire une exception personnalisée étendant ControlThrowable code> cependant, Ce code était destiné à vous dire que Ne pas attraper et supprimer ControlThrowable . Si le code était

import scala.util.control.Breaks
import scala.util.control.ControlThrowable
val b = new Breaks

b.breakable {
  try {
    for (num <- 1 to 10) {
      num match {
        case 5 => throw new RuntimeException("5")
        case 6 => b.break
        case x => println(x)
      }
    }
  } catch {
     case c: Throwable => println(c)
  }
}

alors, il pourrait être plus facile de comprendre ce que le code essaie de dire.

Pour info, scala 2.12 a un bogue qui il n'y a aucun moyen de supprimer ControlThrowable.

try {
  // Body might throw arbitrarily
} catch {
  case c: ControlThrowable => throw c // propagate
  case t: Throwable        => log(t)  // log and suppress
}

Ce code doit afficher des nombres jusqu'à 10 car b.break produit ControlThrowable et il est supprimé avant que b.breakable ne l’obtienne. Mais à cause du bogue, il imprime jusqu'à 5.

Donc, en utilisant scala 2.12, ControlThrowable sera propagé de toute façon. Il ne nécessite pas la ligne case c: ControlThrowable => throw c // propager . Mais à des fins de migration, vous ne devriez pas l'écrire.

En fait, ce bogue sera corrigé dans la version 2.13.x https://github.com/scala/scala/pull/7413


1 commentaires

Oui, attraper ControlThrowable avant Throwable me semble parfaitement logique et en fait je l'ai dans mon code.



2
votes

Je voudrais prolonger la réponse de Brian McCutchon. Je pense qu'il y a plusieurs aspects différents (hautement philosophiques) qui méritent d'être pris en compte.

On pourrait soutenir que la raison pour laquelle vous pouvez hériter directement de Throwable est que parce que le système de type Java n'a pas de moyen d'interdire une telle extension, et que les Throwable s La hiérarchie n'a pas été conçue pour ajouter plus de classes qui ne sont pas des sous-classes de Error ou Exception . C'est probablement la raison pour laquelle des éléments tels que NonLocalReturnException ou BreakException étaient à l'origine des sous-types de RuntimeException plutôt que directement Throwable .

Un autre point est que lors de l'introduction d'un tel marqueur, il devait s'agir d'un trait . Et encore une fois dans le système de type Scala, il n'y a aucun moyen de faire en sorte que les classes qui mélangeront ce trait ne soient pas des sous-classes de Exception .

Ensemble, ces deux faits signifient qu'il pourrait y avoir et qu'il y avait en fait historiquement des sous-classes de ControlThrowable (puis de ControlException ) qui sont des sous-classes de Exception . En gardant cela à l'esprit, il devient clair que les ensembles de types capturés dans ces deux cas n'étaient pas disjoints par conception. Oui, presque un an plus tard, après la introduction de ControlException il s'agissait de NonFatal , qui inclut un test pour ControlThrowable , à la place.


2 commentaires

Y a-t-il un scénario où je mélangerais dans ControlThrowable ? Je pensais que ce trait était uniquement destiné à l'usage interne de Scala.


@lfk, c'est à usage interne mais il n'y a aucun moyen de vous interdire de l'utiliser. Je peux imaginer certains scénarios dans lesquels une bibliothèque avancée (et basée sur des macros) pourrait souhaiter avoir des fonctionnalités de flux de contrôle non locales et utiliser une technique similaire. Un domaine de ces bibliothèques pourrait être une sorte de prise en charge asynchrone telle que scala-async (AFAIK it n'utilise pas ControlThrowable mais je peux imaginer quelque chose de similaire qui pourrait l'utiliser) un autre domaine similaire est une sorte de concurrence coopérative aka prise en charge des co-routines.