¿Alguien puede explicar por qué se pueden lanzar excepciones fuera de la mónada IO, pero solo se pueden atrapar dentro de ella?¿Por qué las excepciones de Haskell solo pueden quedar atrapadas dentro de la mónada IO?
Respuesta
Una de las razones es denotational semantics of Haskell.
Una de las propiedades claras de las funciones de Haskell (pura) es su monotonicidad: un argumento más definido produce un valor más definido. Esta propiedad es muy importante, p. razonar sobre las funciones recursivas (lea el artículo para entender por qué).
Denotación de excepción por definición es la parte inferior, _|_
, el elemento menos en poset correspondiente al tipo dado. Por lo tanto, para satisfacer el requisito de monotonía, la siguiente desigualdad tiene que mantener durante toda la denotación f
de la función Haskell:
f(_|_) <= f(X)
Ahora, si pudiéramos capturar excepciones, podríamos romper esta desigualdad por "reconocimiento" de la parte inferior (para controlar el excepción) y devolver el 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í está la demostración de trabajo completo (requiere base 4 Control.Exception):
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 por iguales y obtener otra respuesta. (Igual en sentido denotacional, es decir, denotan el mismo valor, no en el sentido ==
)
¿Cómo lo haríamos? Considere la siguiente expresión:
let x = x in x
Ésta es una recursividad no termina, por lo que nunca nos devuelve ninguna información y por lo tanto se denota también por _|_
. Si hemos sido capaces de capturar las excepciones, podemos escribir la función f como
f undefined = 0
f (let x = x in x) = _|_
(El último siempre es cierto para las funciones estrictas, ya que Haskell no proporciona medios para detectar la computación no terminar - y no puede, en principio, debido a Halting problem.)
Porque las excepciones pueden rotura referential transparency.
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 estás lamentando no ser capaz de detectar errores como éste entonces afirmo a usted que las funciones no deben ser confiando en error
o cualquier otra excepción, sino que deben utilizar un tipo de retorno adecuada (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 ser de señales como las condiciones de falta de memoria que son completamente independientes del valor que se calcula. Esto claramente no es un concepto puro y debe vivir en IO.
¿Puede dar un ejemplo de cómo romper la transparencia referencial de esta manera? –
Entonces, ¿quieres decirme que los elogios no son la forma de pensar funcionalmente, así que generalmente debo tratar de evitarlos? ¡Estupendo! – fuz
Roman: Lo que mencioné fue que si pudieras capturar excepciones en código puro y basar tu resultado en esas excepciones, dicha acción rompería la transparencia referencial. –
Podría estar equivocado en mi explicación, pero así es como lo entiendo.
Dado que las funciones son puras en Haskell, el compilador tiene el derecho de evaluarlas en el orden que desee y sigue produciendo el mismo resultado. Por ejemplo, la 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
de 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 test
cuando el valor es realmente necesario. En ese momento, la pila de llamadas puede ser completamente diferente de lo que cabría esperar en Java.
Entonces, incluso si quisiéramos captar una posible excepción lanzada por square
, ¿dónde debe colocar la pieza catch
?
- 1. Haskell mónada: IO [doble] a [IO Doble]
- 2. Haskell Extensible IO Exceptions?
- 3. ¿Cómo agregar IO a mi propia mónada en Haskell?
- 4. ¿Por qué el transformador de mónada ListT se considera defectuoso? ¿Qué leyes de mónada se rompen?
- 5. ¿Cómo y por qué funciona la mónada Haskell Cont?
- 6. ¿Las excepciones quedan atrapadas en lambdas en javascript/node.js?
- 7. Haskell: ¿Qué mónada acabo de reinventar?
- 8. IO y quizás Interacción de mónada
- 9. ¿Por qué las propiedades no pueden ser de solo lectura?
- 10. Haskell: FIFO mónada
- 11. ¿Cuál es el significado de las acciones de IO dentro de funciones puras?
- 12. Haskell ReaderT Env IO estándar
- 13. Resultado de la mónada dentro del transformador de mónada
- 14. Haskell arroyos con efectos IO
- 15. Haskell "excepciones"
- 16. ¿Por qué las clases estáticas solo pueden tener miembros estáticos?
- 17. Haskell. Non IO Exception handling
- 18. MonadPlus definición para Haskell IO
- 19. Haskell IO Testing
- 20. Auditoría Java: sistema para detectar excepciones lanzadas/atrapadas (¿aop?)
- 21. IO dentro de Get Monad
- 22. Excepciones puras en Haskell
- 23. ¿Por qué las excepciones de AppDomain invariablemente terminan la aplicación?
- 24. ¿Cómo funcionan las excepciones en Haskell?
- 25. recursiva IO en Haskell
- 26. ¿Existe alguna referencia autorizada sobre qué excepciones pueden arrojarse por las funciones incorporadas de Matlab?
- 27. mónada
- 28. ¿Por qué las excepciones de .NET son mutables?
- 29. Problema con IO "Looping" en Haskell
- 30. Mapeo sobre IO en Haskell
Bien. Este parece ser uno de los antecedentes matemáticos difíciles detrás de Haskell. Gracias por tu breve descripción. – fuz
Lo siento, pero esta respuesta es completamente desconcertante por lo que creo que es una pregunta muy simple ... pero esto probablemente se refleja más en mi falta de comprensión de Haskell en lugar de su respuesta. –
dodgy_coder: Lamento que no haya sido útil para ti. Puede haber diferentes perspectivas desde las cuales uno puede responder esta pregunta. Podría decir que no puede (no) hacerlo porque los tipos de funciones correspondientes lo permiten (no). Entonces, por supuesto, podría preguntar por qué los tipos son como son. La respuesta es "hay buenas razones para (no) permitir tales cosas", e intenté esbozar estas razones más arriba. –