pero bloquear el software de su cliente todavía no es algo bueno
Ciertamente es algo bueno.
Desea cualquier cosa que deje el sistema en un estado indefinido para detener el sistema porque un sistema indefinido puede hacer cosas desagradables como datos corruptos, formatear el disco duro y enviar correos electrónicos amenazadores al presidente. Si no puede recuperarse y volver a poner el sistema en un estado definido, lo más responsable es que se bloquee. Es exactamente por eso que construimos sistemas que se bloquean en lugar de desgarrarse silenciosamente. Ahora, claro, todos queremos un sistema estable que nunca se cuelgue, pero solo queremos eso cuando el sistema se mantenga en un estado seguro predecible y definido.
He oído que las excepciones como control de flujo se consideran un antipatrón serio
Eso es absolutamente cierto, pero a menudo se malinterpreta. Cuando inventaron el sistema de excepción temían estar rompiendo la programación estructurada. La programación estructurada es por eso que tenemos for
, while
, until
, break
, y continue
cuando todo lo que necesitamos, para hacer todo eso, es goto
.
Dijkstra nos enseñó que usar goto de manera informal (es decir, saltar donde quieras) hace que leer códigos sea una pesadilla. Cuando nos dieron el sistema de excepción temieron que estaban reinventando el goto. Entonces nos dijeron que no "lo usáramos para controlar el flujo" con la esperanza de que lo entendiéramos. Desafortunadamente, muchos de nosotros no lo hicimos.
Curiosamente, a menudo no abusamos de las excepciones para crear código de espagueti como solíamos hacerlo con goto. El consejo en sí parece haber causado más problemas.
Fundamentalmente, las excepciones son sobre rechazar una suposición. Cuando solicita que se guarde un archivo, asume que el archivo puede y será guardado. La excepción que obtienes cuando no puede ser puede ser que el nombre sea ilegal, que el HD esté lleno o que una rata haya roído tu cable de datos. Puede manejar todos esos errores de manera diferente, puede manejarlos de la misma manera, o puede dejar que detengan el sistema. Hay un camino feliz en su código donde sus suposiciones deben ser ciertas. De una forma u otra, las excepciones lo llevan por ese camino feliz. Estrictamente hablando, sí, eso es una especie de "control de flujo", pero eso no es de lo que te estaban advirtiendo. Hablaban de tonterías como esta :
"Las excepciones deben ser excepcionales". Esta pequeña tautología nació porque los diseñadores de sistemas de excepción necesitan tiempo para construir trazas de pila. En comparación con saltar, esto es lento. Come tiempo de CPU. Pero si está a punto de iniciar sesión y detener el sistema o al menos detener el procesamiento intensivo de tiempo actual antes de comenzar el siguiente, entonces tiene algo de tiempo para matar. Si las personas comienzan a usar excepciones "para el control de flujo", esas suposiciones sobre el tiempo desaparecen. Entonces, "Las excepciones deben ser excepcionales" realmente se nos dieron como una consideración de rendimiento.
Mucho más importante que eso no nos confunde. ¿Cuánto tiempo te llevó detectar el bucle infinito en el código anterior?
NO devuelva códigos de error.
... es un buen consejo cuando estás en una base de código que normalmente no usa códigos de error. ¿Por qué? Porque nadie recordará guardar el valor de retorno y verificar sus códigos de error. Todavía es una buena convención cuando estás en C.
OneOf
Estás utilizando otra convención más. Eso está bien siempre que establezca la convención y no simplemente pelee con otra. Es confuso tener dos convenciones de error en la misma base de código. Si de alguna manera se ha deshecho de todo el código que usa la otra convención, continúe.
Me gusta la convención a mí mismo. Una de las mejores explicaciones que encontré aquí * :
Pero por mucho que me guste, todavía no voy a mezclarlo con las otras convenciones. Elige uno y quédate con él. 1
1: Con lo cual quiero decir, no me hagas pensar en más de una convención al mismo tiempo.
Pensamientos posteriores
De esta discusión lo que me llevo actualmente es lo siguiente:
- Si espera que la persona que llama inmediatamente detecte y maneje la excepción la mayor parte del tiempo y continúe su trabajo, tal vez a través de otra ruta, probablemente debería ser parte del tipo de retorno. Opcional o OneOf puede ser útil aquí.
- Si espera que la persona que llama de inmediato no detecte la excepción la mayor parte del tiempo, lance una excepción para evitar la tontería de pasarla manualmente por la pila.
- Si no está seguro de lo que va a hacer la persona que llama de inmediato, quizás proporcione ambos, como Parse y TryParse.
Realmente no es tan simple. Una de las cosas fundamentales que debes entender es qué es un cero.
¿Cuántos días quedan en mayo? 0 (porque no es mayo. Ya es junio).
Las excepciones son una forma de rechazar una suposición, pero no son la única forma. Si usa excepciones para rechazar la suposición, deja el camino feliz. Pero si elige valores para enviar el camino feliz que indica que las cosas no son tan simples como se suponía, entonces puede permanecer en ese camino siempre que pueda lidiar con esos valores. A veces, 0 ya se usa para significar algo, por lo que debe encontrar otro valor para mapear su supuesto rechazando la idea. Puede reconocer esta idea por su uso en álgebra antigua . Las mónadas pueden ayudar con eso, pero no siempre tiene que ser una mónada.
Por ejemplo 2 :
IList<int> ParseAllTheInts(String s) { ... }
¿Puedes pensar en alguna buena razón para que esto deba diseñarse de manera que arroje algo deliberadamente? ¿Adivina lo que obtienes cuando no se puede analizar int? Ni siquiera necesito decírtelo.
Esa es una señal de un buen nombre. Lo siento, pero TryParse no es mi idea de un buen nombre.
A menudo evitamos lanzar una excepción al no obtener nada cuando la respuesta podría ser más de una cosa al mismo tiempo, pero por alguna razón si la respuesta es una cosa o nada, nos obsesionamos con insistir en que nos dé una cosa o lanzar:
IList<Point> Intersection(Line a, Line b) { ... }
¿Las líneas paralelas realmente necesitan causar una excepción aquí? ¿Es realmente tan malo si esta lista nunca contendrá más de un punto?
Quizás semánticamente no puedes soportar eso. Si es así, es una pena. Pero tal vez las mónadas, que no tienen un tamaño arbitrario como las List
tiene, te harán sentir mejor al respecto.
Maybe<Point> Intersection(Line a, Line b) { ... }
Las Mónadas son pequeñas colecciones de propósito especial que están destinadas a ser utilizadas de manera específica para evitar tener que probarlas. Se supone que debemos encontrar formas de lidiar con ellos independientemente de lo que contengan. De esa manera, el camino feliz sigue siendo simple. Si se abre y prueba cada mónada que toca, las está usando mal.
Lo sé, es raro. Pero es una herramienta nueva (bueno, para nosotros). Así que dale algo de tiempo. Los martillos tienen más sentido cuando dejas de usarlos en los tornillos.
Si me permites, me gustaría abordar este comentario:
¿Cómo es que ninguna de las respuestas aclara que O mónada no es un código de error, y tampoco lo es OneOf? Son fundamentalmente diferentes y, en consecuencia, la pregunta parece estar basada en un malentendido. (Aunque en forma modificada, sigue siendo una pregunta válida). - Konrad Rudolph 4 de junio de 18 a 14:08
Esto es absolutamente cierto. Las mónadas están mucho más cerca de las colecciones que las excepciones, las banderas o los códigos de error. Ellos hacen contenedores finos para tales cosas cuando se usan sabiamente.
Result
;-)