opciones - ¿Por qué las excepciones de Haskell solo se pueden capturar dentro de la mónada IO?
imprimir en haskell (3)
Podría estar equivocado en mi explicación, pero así es como lo entiendo.
Como las funciones son puras en Haskell, el compilador tiene el derecho de evaluarlas en el orden que desee y aún produce el mismo resultado. Por ejemplo, función dada:
square :: Int -> Int
square x = x * x
expresión square (square 2)
se puede evaluar de diferentes maneras, pero siempre se reduce al mismo resultado que es 16.
Si llamamos square
desde algún otro lugar:
test x = if x == 2
then square x
else 0
square x
se puede evaluar más adelante, "fuera" de la función de test
cuando el valor es realmente necesario. En ese momento, la pila de llamadas puede ser completamente diferente de la que cabría esperar en Java.
Entonces, incluso si quisiéramos capturar una posible excepción lanzada por square
, ¿dónde debería colocar la pieza de catch
?
¿Alguien puede explicar por qué se pueden lanzar excepciones fuera de la mónada IO, pero solo pueden quedar atrapadas dentro de ella?
Porque las excepciones pueden romper la transparencia referencial .
Probablemente esté hablando de excepciones que en realidad son resultados directos de la entrada. Por ejemplo:
head [] = error "oh no!" -- this type of exception
head (x:xs) = x
Si se lamenta por no poder detectar errores como este, entonces le aseguro que las funciones no deberían basarse en el error
ni en ninguna otra excepción, sino que deberían usar un tipo de devolución adecuado ( Maybe
, Either
o quizás MonadError ). Esto te obliga a lidiar con la condición excepcional de una manera más explícita.
A diferencia de lo anterior (y lo que causa el problema detrás de su pregunta), las excepciones pueden provenir de señales como las condiciones de memoria insuficiente que son completamente independientes del valor que se calcula. Esto claramente no es un concepto puro y debe vivir en IO.
Una de las razones es la semántica denotacional de Haskell .
Una de las propiedades claras de las funciones (puras) de Haskell es su monotonicidad: un argumento más definido produce un valor más definido. Esta propiedad es muy importante, por ejemplo, para razonar acerca de las funciones recursivas (lea el artículo para comprender por qué).
La denotación de excepción por definición es la parte inferior, _|_
, el elemento mínimo en poset correspondiente al tipo dado. Por lo tanto, para satisfacer el requisito de monotonicidad, la siguiente desigualdad debe mantenerse para cualquier denotación f
de la función de Haskell:
f(_|_) <= f(X)
Ahora, si pudiéramos detectar excepciones, podríamos romper esta desigualdad "reconociendo" la parte inferior (captando la excepción) y devolviendo un valor más definido:
f x = case catch (seq x True) (/exception -> False) of
True -> -- there was no exception
undefined
False -> -- there was an exception, return defined value
42
Aquí hay una demostración de trabajo completa (requiere Control-4 base-Excepción):
import Prelude hiding (catch)
import System.IO.Unsafe (unsafePerformIO)
import qualified Control.Exception as E
catch :: a -> (E.SomeException -> a) -> a
catch x h = unsafePerformIO $ E.catch (return $! x) (return . h)
f x = case catch (seq x True) (/exception -> False) of
True -> -- there was no exception
undefined
False -> -- there was an exception, return defined value
42
Otra razón, como señaló TomMD, es romper la transparencia referencial. Podrías reemplazar cosas iguales con iguales y obtener otra respuesta. (Igual en el sentido denotacional, es decir, denotan el mismo valor, no en el sentido ==
).
¿Cómo haríamos esto? Considera la siguiente expresión:
let x = x in x
Esta es una recursión sin terminación, por lo que nunca nos devuelve ninguna información y, por lo tanto, también se denota por _|_
. Si pudiéramos detectar excepciones, podríamos escribir la función f tal como
f undefined = 0
f (let x = x in x) = _|_
(Lo último siempre es cierto para funciones estrictas, porque Haskell no proporciona ningún medio para detectar cálculos no terminados, y no puede, en principio, debido al problema de la Detención ).