16

Je suis surpris de ne pas trouver de réponse à cela. J'écris un roguelike et j'utilise la bibliothèque ncurses de hackage, qui est un très bon wrapper autour de la bibliothèque ncurses. Maintenant ncurses a cette bizarrerie où si vous essayez d'écrire le caractère en bas à droite, il le fait, puis il essaie de déplacer le curseur sur le caractère suivant, puis il échoue parce qu'il n'y a nulle part où le déplacer. Il renvoie une valeur d'erreur que vous ne pouvez ignorer. Mon problème est que l'auteur de la bibliothèque haskell ncurses vérifie scrupuleusement les erreurs sur tous les appels, et lorsqu'il y en a un, il appelle: error "drawText: etc etc.". Pour contourner ce problème, dans d'autres langages, comme c ou python, vous êtes obligé d'ignorer l'erreur ou d'attraper et d'ignorer l'exception, mais pour la vie de moi, je n'arrive pas à comprendre comment le faire en haskell. La fonction d'erreur est-elle irrécupérable?Comment attraper (et ignorer) un appel à la fonction d'erreur?

Je vais modifier la bibliothèque localement pour ne pas vérifier les erreurs sur cette fonction si je dois, mais je déteste faire cela. Je suis également ouvert à toute solution de contournement qui me permettrait de dessiner ce dernier caractère sans déplacer le curseur, mais je ne pense pas que ce soit possible.

+0

Hoogle pour "catch" [1]. Deuxième lien vers le bas. [1] http://haskell.org/hoogle/?hoogle=catch –

+0

Malheureusement, l'erreur en question n'est pas dans la monade IO. Eh bien, il commence dans IO, alors vous allez runCurses, qui est la monade Curses, puis updateWindow, qui est la monade de mise à jour. Par conséquent, je ne pense pas que la réponse de Paul fonctionnera. Mais Luqui a l'air d'avoir du potentiel et je vais l'essayer quand je serai à la maison. –

+0

regardant par-dessus ncurses J'ai l'impression que ça ne marchera pas. 'drawText' n'appelle pas' error', il délègue directement à une fonction C. Et il retourne dans la monade 'Update', qui est' ReaderT Window IO a' = 'Fenêtre -> IO a', donc 'unsafeCleanup' ne fonctionnera que s'il a des erreurs lors de la génération de cette * fonction *, pas quand exécuter l'action (peu probable). Je pense que vos options sont: attraper l'erreur dans IO au niveau supérieur, ou ouvrir la source curses pour vous permettre d'injecter une fonction 'catch' plus locale. (Il peut être fait facilement, casse juste l'encapsulation) – luqui

Répondre

12

error est supposé être aussi observable qu'une boucle infinie. Vous pouvez seulement attraper error dans IO, ce qui revient à dire "oui vous pouvez si vous connaissez la magie". Mais à partir de la partie vraiment agréable de Haskell, du code pur, il est irrécupérable, et il est donc fortement conseillé de l'utiliser dans votre code, seulement autant que vous utiliseriez une boucle infinie comme code d'erreur. Ncurses est grossier et vous oblige à faire de la magie pour le corriger.

Je dirais unsafePerformIO serait justifié de le nettoyer. À part ça, c'est en grande partie la même chose que la réponse de Paul.

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) 

Puis enroulez autour unsafeCleanup une valeur qui évaluerait à une erreur de la transformer en un Maybe.

+0

btw, 'Exc.catch (Exc.evaluate (Just $! x)) handler' serait légèrement plus propre imho – hvr

+1

FWIW ceci est maintenant implémenté dans la bibliothèque [spoon] (http://hackage.haskell.org/package/spoon). – luqui

+0

Est-ce que cuoon peut aussi faire 'a -> Sither String a' au lieu de' a -> Maybe a' pour que je puisse voir le message de l'erreur qui a été interceptée? Ou serait-ce contre-indiqué? –

15

Vous pouvez le faire en utilisant catch à partir de Control.Exception. Notez, cependant, que vous devez être dans la monade IO pour ce faire.

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

Je pense que le caractère '$' char dans le putStrLn pourrait être supprimé xD. Mais cela devrait être la réponse acceptée, car il est plus propre – dani24