Algunos idiomas afirman no tener "excepciones de tiempo de ejecución" como una clara ventaja sobre otros idiomas que los tienen.
Estoy confundido sobre el asunto.
La excepción de tiempo de ejecución es solo una herramienta, que yo sepa, y cuando se usa bien:
- puede comunicar estados "sucios" (arrojar datos inesperados)
- agregando pila puede apuntar a la cadena de error
- puede distinguir entre el desorden (por ejemplo, devolver un valor vacío en una entrada no válida) y el uso inseguro que necesita atención de un desarrollador (por ejemplo, lanzar una excepción en una entrada no válida)
- puede agregar detalles a su error con el mensaje de excepción que proporciona más detalles útiles que ayudan a los esfuerzos de depuración (teóricamente)
Por otro lado, me resulta muy difícil depurar un software que "traga" excepciones. P.ej
try {
myFailingCode();
} catch {
// no logs, no crashes, just a dirty state
}
Entonces, la pregunta es: ¿cuál es la fuerte ventaja teórica de no tener "excepciones de tiempo de ejecución"?
Ejemplo
No hay errores de tiempo de ejecución en la práctica. No nulo No indefinido no es una función.
Respuestas:
Las excepciones tienen una semántica extremadamente limitante. Deben manejarse exactamente donde se lanzan, o en la pila de llamadas directas hacia arriba, y no hay ninguna indicación para el programador en el momento de la compilación si se olvida de hacerlo.
Compare esto con Elm, donde los errores se codifican como Resultados o Maybes , que son ambos valores . Eso significa que obtendrá un error del compilador si no maneja el error. Puede almacenarlos en una variable o incluso en una colección para diferir su manejo en un momento conveniente. Puede crear una función para manejar los errores de una manera específica de la aplicación en lugar de repetir bloques try-catch muy similares por todo el lugar. Puede encadenarlos en un cálculo que tenga éxito solo si todas sus partes tienen éxito, y no tienen que estar agrupados en un bloque de prueba. No está limitado por la sintaxis incorporada.
Esto no es nada como "tragar excepciones". Hace explícitas las condiciones de error en el sistema de tipos y proporciona semánticas alternativas mucho más flexibles para manejarlas.
Considere el siguiente ejemplo. Puede pegar esto en http://elm-lang.org/try si desea verlo en acción.
Tenga
String.toInt
en cuenta que en lacalculate
función tiene la posibilidad de fallar. En Java, esto tiene el potencial de generar una excepción de tiempo de ejecución. A medida que lee la entrada del usuario, tiene muchas posibilidades de hacerlo. En cambio, Elm me obliga a lidiar con eso devolviendo unResult
, pero tenga en cuenta que no tengo que lidiar con eso de inmediato. Puedo duplicar la entrada y convertirla en una cadena, luego verificar si hay una entrada incorrecta en lagetDefault
función. Este lugar es mucho más adecuado para la verificación que el punto donde ocurrió el error o hacia arriba en la pila de llamadas.La forma en que el compilador fuerza nuestra mano también es mucho más fina que las excepciones comprobadas de Java. Debe utilizar una función muy específica, como
Result.withDefault
extraer el valor que desea. Si bien técnicamente podrías abusar de ese tipo de mecanismo, no tiene mucho sentido. Como puede diferir la decisión hasta que conozca un buen mensaje predeterminado / de error, no hay razón para no usarlo.fuente
That means you get a compiler error if you don't handle the error.
- Bueno, ese fue el razonamiento detrás de Checked Exceptions en Java, pero todos sabemos lo bien que funcionó.Maybe
,Either
, etc. miradas Elm como si fuera tomando una página de lenguajes como ML, OCaml o Haskell.x = some_func()
, no tengo que hacer nada a menos que quiera examinar el valor dex
, en cuyo caso puedo verificar si tengo un error o un valor "válido"; Además, es un error de tipo estático intentar usar uno en lugar del otro, por lo que no puedo hacerlo. Si los tipos de Elm funcionan de manera similar a otros lenguajes funcionales, ¡en realidad puedo hacer cosas como componer valores de diferentes funciones antes de saber si son errores o no! Esto es típico de los lenguajes FP.Para entender esta afirmación, primero tenemos que entender qué nos compra un sistema de tipo estático. En esencia, lo que nos da un sistema de tipo estático es una garantía: si el tipo de programa verifica, no puede ocurrir una determinada clase de comportamientos de tiempo de ejecución.
Eso suena siniestro. Bueno, un verificador de tipo es similar a un verificador de teoremas. (En realidad, según el isomorfismo de Curry-Howard, son lo mismo.) Una cosa que es muy peculiar acerca de los teoremas es que cuando se prueba un teorema, se demuestra exactamente lo que dice el teorema, nada más. (Por ejemplo, cuando alguien dice "He demostrado que este programa es correcto", siempre debe preguntar "defina 'correcto'"). Lo mismo es cierto para los sistemas de tipos. Cuando decimos "es un programa de tipo seguro", lo que queremos decir es que no que no se produzca ninguna posibilidad de error. Solo podemos decir que los errores que el sistema de tipos nos promete evitar no pueden ocurrir.
Por lo tanto, los programas pueden tener infinitos comportamientos de tiempo de ejecución diferentes. De ellos, infinitamente muchos son útiles, pero también infinitamente muchos son "incorrectos" (para varias definiciones de "corrección"). Un sistema de tipo estático nos permite demostrar que un cierto conjunto finito y fijo de esos infinitos comportamientos incorrectos de tiempo de ejecución no puede ocurrir.
La diferencia entre los diferentes sistemas de tipos radica básicamente en cuál, cuántos y cuán complejos pueden ser los comportamientos de tiempo de ejecución que no se producen. Los sistemas de tipo débil como el de Java solo pueden probar cosas muy básicas. Por ejemplo, Java puede demostrar que un método que se escribe como devolver a
String
no puede devolver aList
. Pero, por ejemplo, puede no demostrar que el método no no volverá. Tampoco puede probar que el método no arroje una excepción. Y no puede probar que no devolverá el errorString
; cualquieraString
satisfará el verificador de tipo. (Y, por supuesto, inclusonull
satisfaga él también.) Incluso hay cosas muy simples que Java no se puede demostrar, por lo que tenemos excepciones comoArrayStoreException
,ClassCastException
o favorito de todos, elNullPointerException
.Los sistemas de tipos más potentes como el de Agda también pueden probar cosas como "devolverá la suma de los dos argumentos" o "devuelve la versión ordenada de la lista pasada como argumento".
Ahora, lo que los diseñadores de Elm quieren decir con la afirmación de que no tienen excepciones de tiempo de ejecución es que el sistema de tipos de Elm puede demostrar la ausencia de (una porción significativa de) comportamientos de tiempo de ejecución que en otros idiomas no se puede demostrar que no ocurran y, por lo tanto, podrían conducir a a un comportamiento erróneo en tiempo de ejecución (que en el mejor de los casos significa una excepción, en el peor de los casos significa un bloqueo, y en el peor de los casos significa sin bloqueo, sin excepción y simplemente un resultado silenciosamente incorrecto).
Por lo tanto, están no diciendo "no implementamos excepciones". Están diciendo "cosas que serían excepciones de tiempo de ejecución en lenguajes típicos con los que los programadores típicos que llegarían a Elm tendrían experiencia, son atrapados por el sistema de tipos". Por supuesto, alguien que viene de Idris, Agda, Guru, Epigram, Isabelle / HOL, Coq o idiomas similares verá a Elm como bastante débil en comparación. La declaración está más dirigida a los programadores típicos de Java, C♯, C ++, Objective-C, PHP, ECMAScript, Python, Ruby, Perl, ...
fuente
Elm no puede garantizar ninguna excepción de tiempo de ejecución por la misma razón C no puede garantizar ninguna excepción de tiempo de ejecución: el lenguaje no admite el concepto de excepciones.
Elm tiene una forma de señalar las condiciones de error en tiempo de ejecución, pero este sistema no es una excepción, es "Resultados". Una función que puede fallar devuelve un "Resultado" que contiene un valor regular o un error. Elms está fuertemente tipado, por lo que esto es explícito en el sistema de tipos. Si una función siempre devuelve un entero, tiene el tipo
Int
. Pero si devuelve un entero o falla, el tipo de retorno esResult Error Int
. (La cadena es el mensaje de error). Esto lo obliga a manejar explícitamente ambos casos en el sitio de la llamada.Aquí hay un ejemplo de la introducción (un poco simplificado):
La función
toInt
puede fallar si la entrada no es analizable, por lo que su tipo de retorno sí lo esResult String int
. Para obtener el valor entero real, debe "desempaquetar" mediante la coincidencia de patrones, lo que a su vez lo obliga a manejar ambos casos.Los resultados y excepciones fundamentalmente hacen lo mismo, la diferencia importante son los "valores predeterminados". Las excepciones aparecerán y terminarán el programa de manera predeterminada, y debe atraparlas explícitamente si desea manejarlas. El resultado es al revés: se ve obligado a manejarlos de manera predeterminada, por lo que debe pasarlos de manera explícita al principio si desea que finalicen el programa. Es fácil ver cómo este comportamiento puede conducir a un código más robusto.
fuente
doSomeStuff(x: Int): Int
. Normalmente espera que devuelva unInt
, pero ¿puede arrojar una excepción también? Sin mirar su código fuente, no puedes saberlo. Por el contrario, un lenguaje B que codifica errores a través de tipos puede tener la misma función declarada así:doSomeStuff(x: Int): ErrorOrResultOfType<Int>
(en Elm, este tipo se denomina realmenteResult
). A diferencia del primer caso, ahora es inmediatamente obvio si la función puede fallar, y debe manejarla explícitamente.this is how you program in languages such as ML or Haskell
En Haskell, sí; ML, no. Robert Harper, uno de los principales contribuyentes de Standard ML e investigador del lenguaje de programación, considera que las excepciones son útiles . Los tipos de error pueden interferir con la composición de la función en los casos en que puede garantizar que no se produzca un error. Las excepciones también tienen un rendimiento diferente. No paga por las excepciones que no se lanzan, pero paga por verificar los valores de error cada vez, y las excepciones son una forma natural de expresar retroceso en algunos algoritmosPrimero, tenga en cuenta que su ejemplo de excepciones de "deglución" es, en general, una práctica terrible y no tiene relación alguna con no tener excepciones de tiempo de ejecución; cuando lo piensa, tuvo un error de tiempo de ejecución, pero eligió ocultarlo y no hacer nada al respecto. Esto a menudo dará lugar a errores que son difíciles de entender.
Esta pregunta podría interpretarse de muchas maneras, pero como mencionó a Elm en los comentarios, el contexto es más claro.
Elm es, entre otras cosas, un lenguaje de programación estáticamente tipado . Uno de los beneficios de este tipo de sistemas de tipos es que el compilador detecta muchas clases de errores (aunque no todos), antes de que el programa se use realmente. Algunos tipos de errores pueden codificarse en tipos (como Elm's
Result
yTask
), en lugar de arrojarse como excepciones. Esto es lo que quieren decir los diseñadores de Elm: se detectarán muchos errores en el momento de la compilación en lugar del "tiempo de ejecución", y el compilador lo obligará a tratarlos en lugar de ignorarlos y esperar lo mejor. Está claro por qué esto es una ventaja: mejor que el programador se dé cuenta de un problema antes que el usuario.Tenga en cuenta que cuando no utiliza excepciones, los errores se codifican de otras maneras menos sorprendentes. De la documentación de Elm :
Los diseñadores de Elm son un poco atrevidos al afirmar que "no hay excepciones de tiempo de ejecución" , aunque lo califican con "en la práctica". Lo que probablemente significan es "menos errores inesperados que si estuviera codificando en JavaScript".
fuente
Result
yTask
que se parecen mucho a los más familiaresEither
yFuture
de otros idiomas. A diferencia de las excepciones, los valores de estos tipos se pueden combinar y en algún momento debe manejarlos explícitamente: ¿representan un valor válido o un error? No leo las mentes, pero esta falta de sorpresa para el programador es probablemente lo que los diseñadores de Elm querían decir con "sin excepciones de tiempo de ejecución" :)Elm afirma:
Pero preguntas sobre excepciones de tiempo de ejecución . Hay una diferencia
En Elm, nada devuelve un resultado inesperado. NO puede escribir un programa válido en Elm que produzca errores de tiempo de ejecución. Por lo tanto, no necesita excepciones.
Entonces, la pregunta debería ser:
Si puede escribir código que nunca tenga errores de tiempo de ejecución, sus programas nunca fallarán.
fuente