¿Cómo funcionan las excepciones en Haskell?

86

En GHCi:

Prelude> error (error "")
*** Exception: 
Prelude> (error . error) ""
*** Exception: *** Exception: 

¿Por qué el primero no es una excepción anidada?

Daniel Wagner
fuente
9
Esta es una transformación que GHC puede hacer: "Soy el compilador que opera por sí mismo, y todos los _ | _s son iguales para mí". ¿Está preguntando por los detalles de implementación que hacen que esas dos líneas se compilen de manera diferente?
shachaf
3
errores especial y no es realmente un mecanismo de excepción. Para ver excepciones reales y capturables, consulte Errormónada.
Cat Plus Plus
1
Tenga en cuenta, por ejemplo, que se (\f g x -> f (g x)) error error ""comporta de manera diferente a (.) error error "", aunque esa función es equivalente a (.). Tal vez tenga que ver con los indicadores de optimización con los que se compiló Prelude.
shachaf
5
También iterate error "" !! ny lo impresionante fix error.
Vitus
9
Siempre pretendo eso error = errory programo en consecuencia.
Gabriel Gonzalez

Respuestas:

101

La respuesta es que esta es la semántica (algo sorprendente) de las excepciones imprecisas

Cuando se puede mostrar que el código puro evalúa un conjunto de valores excepcionales (es decir, el valor de erroro undefined, y explícitamente no el tipo de excepciones generadas en IO ), entonces el lenguaje permite que se devuelva cualquier valor de ese conjunto. Los valores excepcionales en Haskell se parecen más NaNal código de punto flotante que a las excepciones basadas en el flujo de control en los lenguajes imperativos.

Un problema ocasional incluso para los Haskellers avanzados es un caso como:

 case x of
   1 -> error "One"
   _ -> error "Not one"

Dado que el código evalúa un conjunto de excepciones, GHC es libre de elegir una. Con las optimizaciones activadas, es posible que descubra que esto siempre se evalúa como "Ninguno".

¿Por qué hacemos esto? Porque de lo contrario restringiríamos demasiado el orden de evaluación del lenguaje, por ejemplo, tendríamos que fijar un resultado determinista para:

 f (error "a") (error "b")

por ejemplo, requiriendo que se evalúe de izquierda a derecha si hay valores de error presentes. ¡Muy poco Haskelly!

Dado que no queremos paralizar las optimizaciones que se pueden hacer en nuestro código solo para brindar soporte error, la solución es especificar que el resultado es una elección no determinista del conjunto de valores excepcionales: ¡excepciones imprecisas! En cierto modo, se devuelven todas las excepciones y se elige una.

Normalmente, no le importa, una excepción es una excepción, a menos que le importe la cadena dentro de la excepción, en cuyo caso usar errorpara depurar es muy confuso.


Referencias: Una semántica para excepciones imprecisas , Simon Peyton Jones, Alastair Reid, Tony Hoare, Simon Marlow, Fergus Henderson. Diseño e implementación de lenguajes de programación Proc (PLDI'99), Atlanta. ( PDF )

Don Stewart
fuente
2
Entiendo que GHC eligió una de las excepciones que se podrían encontrar. Pero en su ejemplo de "caso", la excepción "No una" no se puede encontrar para una entrada de 1, por lo que aún lo clasificaría como un error.
Peaker
8
@Peaker dead code elim - el optimizador no necesita mirar x para ver que el resultado es un error, todas las ramas producen el "mismo" valor, por lo que puede ignorar el valor de entrada por completo. ¡No es un error, con excepciones imprecisas!
Don Stewart
1
@lpsmith: Creo que todos los tipos de excepciones son imprecisos (cuando se lanzan usando throw) y con los que puedes lanzar una excepción de manera determinista throwIO.
FunctorSalad
4
@Peaker, creo que tienes razón. No creo que ghc deba optimizar esta expresión si quieren seguir las reglas establecidas en el documento de excepciones imprecisas.
2012
3
Lo que parece decir el documento de excepciones imprecisas es que si el escrutinio del caso es un valor de error, se pueden devolver valores de error de las ramas. Entonces case error "banana" of (x:xs) -> error "bonobo"puedo darte * Exception: bonobo.
Ben Millwood