16

Me sorprende que no haya podido encontrar una respuesta a esto en cualquier lugar.¿Cómo atrapar (e ignorar) una llamada a la función de error?

Estoy escribiendo un roguelike y estoy usando la biblioteca ncurses de hackage, que es una muy buena envoltura alrededor de la biblioteca de ncurses. Ahora ncurses tiene esta peculiaridad de que si tratas de escribir el personaje de abajo a la derecha, lo hace, entonces intenta mover el cursor al siguiente carácter, luego falla porque no hay ningún lugar donde moverlo. Devuelve un valor de error que solo puede ignorar.

Mi problema es que el escritor de la biblioteca haskell ncurses comprueba diligentemente cualquier error en todas las llamadas, y cuando hay una, llama: error "drawText: etc etc.".

En otros idiomas, como c o python, para evitar esto, se ven obligados a ignorar el error o atrapar e ignorar la excepción, pero por mi vida no puedo encontrar la manera de hacerlo en haskell. ¿Es la función de error irrecuperable?

Voy a modificar la biblioteca localmente para no buscar errores en esa función si es necesario, pero odio hacerlo. También estoy abierto a cualquier solución que me permita dibujar ese último personaje sin mover el cursor, pero no creo que eso sea posible.

+0

Hoogle for "catch" [1]. Segundo enlace abajo. [1] http://haskell.org/hoogle/?hoogle=catch –

+0

Lamentablemente, el error en cuestión no está en la mónada IO. Bueno, comienza en IO, luego vas runCurses, que es la mónada Curses, luego updateWindow, que es la mónada Update. Por lo tanto, no creo que la respuesta de Pablo funcione. Pero parece que luqui tiene potencial y lo intentaré cuando llegue a casa. –

+0

mirando por encima de ncurses Tengo la sensación de que no va a funcionar. 'drawText' no llama' error', sino que delega directamente a una función C. Y vuelve en la mónada 'Update', que es 'ReaderT Window IO a' =' Window -> IO a', por lo que 'unsafeCleanup' solo funcionará si se produce un error al generar esa * función *, no cuando ejecutando la acción (improbable). Creo que sus opciones son: detectar el error en IO en el nivel superior, o abrir la fuente de las maldiciones para permitirle inyectar una función 'catch' más local. (Se puede hacer fácilmente, solo rompe la encapsulación) – luqui

Respuesta

12

Se supone que error es tan observable como un bucle infinito. Solo puedes ver error en IO, que es como decir "sí, puedes si sabes magia". Pero de la parte realmente buena de Haskell, código puro, es irrecuperable, y por lo tanto, se recomienda no para usar en su código, solo la misma cantidad que usaría un bucle infinito como código de error.

ncurses es ser grosero y hacer que hagas magia para corregirlo. Yo diría que unsafePerformIO estaría garantizado para limpiarlo. Aparte de eso, esto es en gran medida lo mismo que la respuesta de Pablo.

import qualified Control.Exception as Exc 

{-# NOINLINE unsafeCleanup #-} 
unsafeCleanup :: a -> Maybe a 
unsafeCleanup x = unsafePerformIO $ Exc.catch (x `seq` return (Just x)) handler 
    where 
    handler exc = return Nothing `const` (exc :: Exc.ErrorCall) 

Luego envuelva unsafeCleanup en torno a cualquier valor que se evalúe a un error de convertirlo en un Maybe.

+0

por cierto, 'Exc.catch (Exc.evaluate (Just $! x)) handler' sería un poco más limpio imho – hvr

+1

FWIW esto ahora está implementado en la biblioteca [spoon] (http://hackage.haskell.org/package/spoon). – luqui

+0

¿Puede la cuchara hacer también 'a -> E String a' en lugar de' a -> Maybe a' para que yo pueda ver el mensaje del error que fue capturado? ¿O estaría eso contraindicado? –

15

Puede hacerlo utilizando catch desde Control.Exception. Sin embargo, tenga en cuenta que debe estar en la mónada IO para hacer esto.

import qualified Control.Exception as Exc 

divide :: Float -> Float -> Float 
divide x 0 = error "Division by 0." 
divide x y = x/y 

main :: IO() 
main = Exc.catch (print $ divide 5 0) handler 
    where 
     handler :: Exc.ErrorCall -> IO() 
     handler _ = putStrLn $ "You divided by 0!" 
+0

Creo que el carácter '$' en putStrLn podría eliminarse xD. Pero esta debería ser la respuesta aceptada, ya que es más limpia – dani24

Cuestiones relacionadas