Hay muchos requisitos necesarios para que un sistema transmita y maneje adecuadamente las excepciones. También hay muchas opciones para elegir un idioma para implementar el concepto.
Requisitos para excepciones (sin ningún orden en particular):
Documentación : un idioma debe tener un medio para documentar excepciones que una API puede generar. Idealmente, este medio de documentación debería ser utilizable en la máquina para permitir que los compiladores e IDE brinden soporte al programador.
Transmitir situaciones excepcionales : esta es obvia, para permitir que una función transmita situaciones que impiden que la funcionalidad llamada realice la acción esperada. En mi opinión, hay tres grandes categorías de tales situaciones:
2.1 Errores en el código que hacen que algunos datos sean inválidos.
2.2 Problemas en la configuración u otros recursos externos.
2.3 Recursos inherentemente poco confiables (red, sistemas de archivos, bases de datos, usuarios finales, etc.). Estos son un poco arrinconados, ya que su naturaleza poco confiable debería hacernos esperar sus fallas esporádicas. En este caso, ¿estas situaciones se consideran excepcionales?
Proporcione suficiente información para que el código lo maneje : las excepciones deben proporcionar suficiente información a la persona que llama para que pueda reaccionar y posiblemente manejar la situación. la información también debe ser suficiente para que, cuando se registre, estas excepciones proporcionen suficiente contexto a un programador para identificar y aislar las declaraciones ofensivas y proporcionar una solución.
Proporcione confianza al programador sobre el estado actual del estado de ejecución de su código : las capacidades de manejo de excepciones de un sistema de software deben estar lo suficientemente presentes como para proporcionar las garantías necesarias mientras se mantiene alejado del programador para que pueda concentrarse en la tarea en mano.
Para cubrir estos, se implementaron los siguientes métodos en varios idiomas:
Excepciones marcadas Proporcionan una excelente manera de documentar excepciones, y teóricamente, cuando se implementa correctamente, debe proporcionar una amplia garantía de que todo está bien. Sin embargo, el costo es tal que muchos sienten que es más productivo eludir simplemente al tragar excepciones o volver a lanzarlas como excepciones no controladas. Cuando se usan excepciones marcadas de manera inapropiada, se pierde toda su utilidad. Además, las excepciones marcadas dificultan la creación de una API estable en el tiempo. Las implementaciones de un sistema genérico dentro de un dominio específico traerá una carga de situación excepcional que sería difícil de mantener utilizando únicamente excepciones marcadas.
Excepciones no verificadas: mucho más versátiles que las excepciones marcadas, no logran documentar adecuadamente las posibles situaciones excepcionales de una implementación determinada. Dependen de la documentación ad-hoc, si es que lo hacen. Esto crea situaciones en las que la naturaleza poco confiable de un medio está enmascarada por una API que da la apariencia de confiabilidad. Además, cuando se lanzan, estas excepciones pierden su significado a medida que avanzan de nuevo a través de las capas de abstracción. Dado que están mal documentados, un programador no puede enfocarse específicamente en ellos y, a menudo, necesita lanzar una red mucho más amplia de la necesaria para garantizar que los sistemas secundarios, en caso de que fallen, no derriben todo el sistema. Lo que nos lleva de vuelta al problema de la deglución, verificó las excepciones proporcionadas.
Tipos de retorno de estado múltiple Aquí es confiar en un conjunto disjunto, tupla u otro concepto similar para devolver el resultado esperado o un objeto que representa la excepción. Aquí no se desenrolla la pila, no se corta el código, todo se ejecuta normalmente, pero el valor de retorno debe validarse por error antes de continuar. Realmente no he trabajado con esto todavía, así que no puedo comentar por experiencia. Reconozco que resuelve algunas excepciones de problemas sin pasar por el flujo normal, pero aún sufrirá los mismos problemas que las excepciones comprobadas como ser tedioso y constantemente "en su cara".
Entonces la pregunta es:
¿Cuál es su experiencia en este asunto y qué, según usted, es el mejor candidato para hacer un buen sistema de manejo de excepciones para un idioma?
EDITAR: Pocos minutos después de escribir esta pregunta, me encontré con esta publicación , ¡espeluznante!
fuente
noexcept
historia en C ++ puede proporcionar muy buenas ideas para EH en C # y Java también.)Respuestas:
En los primeros días de C ++ descubrimos que sin algún tipo de programación genérica, los lenguajes fuertemente tipados eran extremadamente difíciles de manejar. También descubrimos que las excepciones verificadas y la programación genérica no funcionaban bien juntas, y las excepciones verificadas fueron esencialmente abandonadas.
Los tipos de devolución de varios conjuntos son excelentes, pero no reemplazan las excepciones. Sin excepciones, el código está lleno de ruido de comprobación de errores.
El otro problema con las excepciones marcadas es que un cambio en las excepciones generadas por una función de bajo nivel fuerza una cascada de cambios en todas las personas que llaman, y sus llamadas, y así sucesivamente. La única forma de evitar esto es que cada nivel de código capture las excepciones lanzadas por los niveles inferiores y las envuelva en una nueva excepción. Nuevamente, terminas con un código muy ruidoso.
fuente
Durante mucho tiempo los lenguajes OO, el uso de excepciones ha sido el estándar de facto para comunicar errores. Pero los lenguajes de programación funcionales ofrecen la posibilidad de un enfoque diferente, por ejemplo, usando mónadas (que no he estado usando), o la "Programación orientada al ferrocarril" más liviana, como lo describe Scott Wlaschin.
En esta entrada de blog
En esta presentación en NDC 2014
Realmente es una variante del tipo de resultado multiestado.
El tipo de resultado podría declararse así
Entonces, el resultado de una función que devuelve este tipo sería a
Success
o aFail
type. No puede ser ambos.En lenguajes de programación orientados más imperativos, este tipo de estilo podría requerir una gran cantidad de código en el sitio de la persona que llama. Pero la programación funcional le permite construir funciones de enlace u operadores para vincular múltiples funciones para que la verificación de errores no ocupe la mitad del código. Como ejemplo:
La
updateUser
función llama a cada una de estas funciones en sucesión, y cada una de ellas podría fallar. Si todos tienen éxito, se devuelve el resultado de la última función llamada. Si una de las funciones falla, el resultado de esa función será el resultado de laupdateUser
función general . Todo esto lo maneja el operador personalizado >> =.En el ejemplo anterior, los tipos de error podrían ser
Si la persona que llama
updateUser
no maneja explícitamente todos los posibles errores de la función, el compilador emitirá una advertencia. Entonces tienes todo documentado.En Haskell, hay una
do
notación que puede hacer que el código sea aún más limpio.fuente
do
notación de Haskell , que hace que el código resultante sea aún más limpio.Railway Oriented Programming
comportamiento es exactamente monádico.La respuesta de Pete me parece muy buena y me gustaría agregar un poco de consideración y un ejemplo. Una discusión muy interesante sobre el uso de excepciones frente a la devolución de valores de error especiales se puede encontrar en Programación en ML estándar, por Robert Harper , al final de la Sección 29.3, página 243, 244.
El problema es implementar una función parcial que
f
devuelve un valor de algún tipot
. Una solución es hacer que la función tenga tipoy lanzar una excepción cuando no hay resultado posible. La segunda solución es implementar una función con tipo
y retorno
SOME v
al éxito yNONE
al fracaso.Aquí está el texto del libro, con una pequeña adaptación hecha por mí mismo para hacer que el texto sea más general (el libro se refiere a un ejemplo particular). El texto modificado está escrito en cursiva .
Esto en lo que respecta a la elección entre excepciones y tipos de devolución de opciones.
Con respecto a la idea de que representar un error en el tipo de retorno conduce a verificaciones de error repartidas por todo el código: este no tiene por qué ser el caso. Aquí hay un pequeño ejemplo en Haskell que ilustra esto.
Supongamos que queremos analizar dos números y luego dividir el primero por el segundo. Por lo tanto, podría haber un error al analizar cada número o al dividir (división por cero). Entonces tenemos que verificar si hay un error después de cada paso.
El análisis y la división se realizan en el
let ...
bloque. Tenga en cuenta que al usar laMaybe
mónada y lado
notación, solo se especifica la ruta de éxito : la semántica de laMaybe
mónada propaga implícitamente el valor de error (Nothing
). Sin gastos generales para el programador.fuente
Either
tipo sería más adecuado. ¿Qué haces si llegasNothing
aquí? Simplemente recibe el mensaje "error". No es muy útil para la depuración.Me he convertido en un gran admirador de las Excepciones marcadas y me gustaría compartir mi regla general sobre cuándo usarlas.
He llegado a la conclusión de que básicamente hay 2 tipos de errores con los que mi código tiene que lidiar. Hay errores que se pueden probar antes de que se ejecute el código y hay errores que no se pueden probar antes de que se ejecute el código. Un ejemplo simple de un error que es comprobable antes de que el código se ejecute en una NullPointerException.
Una prueba simple podría haber evitado el error como ...
Hay momentos en la informática en los que puede ejecutar 1 o más pruebas antes de ejecutar el código para asegurarse de que está seguro Y TODAVÍA OBTENDRÁ UNA EXCEPCIÓN. Por ejemplo, puede probar un sistema de archivos para asegurarse de que haya suficiente espacio en el disco duro antes de escribir sus datos en la unidad. En un sistema operativo multiprocesamiento, como los que se usan hoy en día, su proceso podría probar el espacio en disco y el sistema de archivos devolverá un valor que indica que hay suficiente espacio, luego un cambio de contexto a otro proceso podría escribir los bytes restantes disponibles para el funcionamiento sistema. Cuando el contexto del sistema operativo vuelve a su proceso en ejecución donde escribe sus contenidos en el disco, se producirá una Excepción simplemente porque no hay suficiente espacio en disco en el sistema de archivos.
Considero el escenario anterior como un caso perfecto para una excepción marcada. Es una excepción en el código que te obliga a lidiar con algo malo a pesar de que tu código podría estar perfectamente escrito. Si elige hacer cosas malas como "tragar la excepción", usted es el mal programador. Por cierto, he encontrado casos en los que es razonable tragar la excepción, pero deje un comentario en el código sobre por qué se tragó la excepción. El mecanismo de manejo de excepciones no tiene la culpa. Comúnmente bromeo diciendo que preferiría que mi marcapasos esté escrito en un idioma que tenga Excepciones marcadas.
Hay momentos en que se hace difícil decidir si el código es comprobable o no. Por ejemplo, si está escribiendo un intérprete y se emite una SyntaxException cuando el código no se ejecuta por alguna razón sintáctica, ¿debería ser SyntaxException una excepción comprobada o (en Java) una excepción RuntimeException? Respondería si el intérprete verifica la sintaxis del código antes de que se ejecute el código, entonces la Excepción debería ser una RuntimeException. Si el intérprete simplemente ejecuta el código 'hot' y simplemente encuentra un error de sintaxis, diría que la excepción debería ser una excepción marcada.
Admitiré que no siempre estoy feliz de tener que atrapar o lanzar una excepción marcada porque hay momentos en los que no estoy seguro de qué hacer. Las excepciones marcadas son una forma de obligar a un programador a tener en cuenta el posible problema que puede ocurrir. Una de las razones por las que programo en Java es porque tiene Excepciones marcadas.
fuente
Actualmente estoy en medio de un proyecto / API bastante grande basado en OOP y he usado este diseño de las excepciones. Pero todo realmente depende de cuán profundo quieras llegar con el manejo de excepciones y similares.
ExpectedException
- AuthorisedException
- EmptySetException
- NoRemainingException
- NoRowsException
- NotFoundException
- ValidationException
UnexpectedException
- ConnectivityException
- EnvironmentException
- ProgrammerException
- SQLException
EJEMPLO
fuente
NAN
oNULL
.