Tal como dice el título: ¿qué garantías hay para evaluar una unidad de retorno de la función Haskell? Uno pensaría que no es necesario ejecutar ningún tipo de evaluación en tal caso, el compilador podría reemplazar todas esas llamadas con un ()
valor inmediato a menos que existan solicitudes explícitas de rigor, en cuyo caso el código podría tener que decidir si debería retorno ()
o fondo.
He experimentado con esto en GHCi, y parece que sucede lo contrario, es decir, tal función parece ser evaluada. Un ejemplo muy primitivo sería
f :: a -> ()
f _ = undefined
La evaluación f 1
arroja un error debido a la presencia de undefined
, por lo que definitivamente ocurre alguna evaluación. Sin embargo, no está claro cuán profunda es la evaluación; a veces parece ir tan profundo como es necesario evaluar todas las llamadas a funciones que regresan ()
. Ejemplo:
g :: [a] -> ()
g [] = ()
g (_:xs) = g xs
Este código se repite indefinidamente si se presenta con g (let x = 1:x in x)
. Pero entonces
f :: a -> ()
f _ = undefined
h :: a -> ()
h _ = ()
se puede usar para mostrar que h (f 1)
devuelve ()
, por lo que en este caso no se evalúan todas las subexpresiones de valores unitarios. ¿Cuál es la regla general aquí?
ETA: por supuesto que sé sobre la pereza. Me pregunto qué impide que los escritores de compiladores hagan que este caso en particular sea aún más vago de lo habitual.
ETA2: resumen de los ejemplos: GHC parece tratar ()
exactamente como cualquier otro tipo, es decir, como si hubiera una pregunta sobre qué valor regular que habita el tipo debe devolverse de una función. El hecho de que solo haya uno de esos valores no parece ser (ab) utilizado por los algoritmos de optimización.
ETA3: cuando digo Haskell, me refiero a Haskell según lo definido por el Informe, no a Haskell-the-H-in-GHC. Parece ser una suposición no compartida tan ampliamente como imaginé (que era 'por el 100% de los lectores'), o probablemente habría podido formular una pregunta más clara. Aun así, lamento haber cambiado el título de la pregunta, ya que originalmente preguntaba qué garantías hay para que se llame a tal función.
ETA4: parece que esta pregunta ha seguido su curso, y lo estoy considerando sin respuesta. (Estaba buscando una función de 'pregunta cerrada' pero solo encontré 'responde tu propia pregunta' y como no se puede responder, no tomé esa ruta). Nadie sacó nada del Informe que lo decidiera de cualquier manera , que me siento tentado a interpretar como una fuerte pero no definitiva respuesta "sin garantía para el idioma como tal". Todo lo que sabemos es que la implementación actual de GHC no omitirá la evaluación de dicha función.
Me he encontrado con el problema real al portar una aplicación OCaml a Haskell. La aplicación original tenía una estructura recursiva de muchos tipos, y el código declaraba una serie de funciones que requerían assert_structureN_is_correct
N en 1..6 o 7, cada una de las cuales devolvía la unidad si la estructura era realmente correcta y lanzaba una excepción si no era así. . Además, estas funciones se llamaban entre sí al descomponer las condiciones de corrección. En Haskell, esto se maneja mejor con la Either String
mónada, así que lo transcribí de esa manera, pero la pregunta como cuestión teórica permaneció. Gracias por todas las entradas y respuestas.
f 1
es "reemplazado" por undefined
en todos los casos.
... -> ()
puede 1) terminar y regresar ()
, 2) terminar con un error de excepción / tiempo de ejecución y no devolver nada, o 3) divergir (recursión infinita). GHC no optimiza el código, suponiendo que solo 1) puede suceder: si f 1
se solicita, no omite su evaluación y devolución ()
. La semántica de Haskell es evaluarla y ver qué sucede entre 1,2,3.
()
(el tipo o el valor) en esta pregunta. Todas las mismas observaciones suceden si reemplazas () :: ()
con, digamos, en 0 :: Int
todas partes. Todas estas son viejas y aburridas consecuencias de la evaluación perezosa.
()
tipo, ()
y undefined
.
h1::()->() ; h1 () = ()
yh2::()->() ; h2 _ = ()
. Ejecute ambosh1 (f 1)
yh2 (f 1)
, y vea que solo el primero exige(f 1)
.