16

Ich bin überrascht, dass ich nirgends eine Antwort darauf finden konnte.Wie kann man einen Aufruf der Fehlerfunktion abfangen (und ignorieren)?

Ich schreibe ein Roguelike und ich benutze die Ncurses-Bibliothek von Hackage, die ein ziemlich guter Wrapper rund um die Ncurses-Bibliothek ist. Nun hat ncurses diese Eigenart, wenn Sie versuchen, das Zeichen unten rechts zu schreiben, dann versucht es, den Cursor zum nächsten Zeichen zu bewegen, dann scheitert es, weil es nirgendwo verschoben werden kann. Es gibt einen Fehlerwert zurück, den Sie nur ignorieren können.

Mein Problem ist, dass der haskell ncurses Bibliotheksschreiber pflichtbewusst nach Fehlern bei allen Aufrufen sucht, und wenn es einen gibt, ruft er: error "drawText: etc etc.".

In anderen Sprachen, wie C oder Python, um dies zu umgehen, sind Sie gezwungen, den Fehler zu ignorieren oder die Ausnahme zu fangen und zu ignorieren, aber für das Leben von mir kann ich nicht herausfinden, wie es in Haskell zu tun. Ist die Fehlerfunktion nicht wiederherstellbar?

Ich werde die Bibliothek lokal ändern, um nicht auf Fehler auf dieser Funktion zu prüfen, wenn ich muss, aber ich hasse es, das zu tun. Ich bin auch offen für jede Problemumgehung, die es mir erlauben würde, das letzte Zeichen zu zeichnen, ohne den Cursor zu bewegen, aber ich glaube nicht, dass das möglich ist.

+0

Hoogle für "fangen" [1]. Zweite Verbindung nach unten. [1] http://haskell.org/hoogle/?hoogle=catch –

+0

Leider ist der betreffende Fehler nicht in der IO-Monade. Nun, es beginnt in IO, dann gehst du runCurses, das ist die Curses-Monade, dann updateWindow, das ist die Update-Monade. Daher glaube ich nicht, dass Pauls Antwort funktionieren wird. Aber Luqui sieht aus, als hätte es Potenzial und ich werde es versuchen, wenn ich nach Hause komme. –

+0

Blick über Ncurses Ich habe das Gefühl, es wird nicht funktionieren. 'drawText' ruft nicht 'error' auf, sondern delegiert direkt an eine C-Funktion. Und es kehrt in der "Update" -Monade zurück, die 'ReaderT Window IOa' =' Window -> IOa' ist, so dass 'unsafeCleanup' nur funktionieren wird, wenn es beim Generieren dieser * Funktion * aussetzt, nicht wenn Ausführen der Aktion (unwahrscheinlich). Ich denke, Ihre Optionen sind: Fangen Sie den Fehler in IO auf der obersten Ebene, oder öffnen Sie die Fluchquelle, damit Sie eine lokalere "Fang" -Funktion injizieren können. (Es kann leicht getan werden, bricht nur Verkapselung) – luqui

Antwort

12

error soll als beobachtbare als Endlosschleife sein. Sie können nur error in IO fangen, was wie "yeah you can, wenn Sie magic wissen" ist. Aber aus dem wirklich schönen Teil von Haskell, reinem Code, ist es nicht behebbar, und daher wird dringend empfohlen, nicht in Ihrem Code zu verwenden, nur so viel wie Sie jemals eine Endlosschleife als Fehlercode verwenden würden.

ncurses wird unhöflich und macht Sie Magie, um es zu korrigieren. Ich würde sagen, unsafePerformIO wäre gerechtfertigt, um es aufzuräumen. Abgesehen davon ist dies weitgehend dasselbe wie die Antwort von Paulus.

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) 

wickeln Dann unsafeCleanup um einen Wert, der auf einen Fehler bewerten wäre es in ein Maybe einzuschalten.

+0

BTW, 'Exc.catch (Exc.evaluate (Nur $! x)) handler 'wäre etwas sauberer imho – hvr

+1

FWIW das ist nun in der [löffel] (http://hackage.haskell.org/package/spoon) library implementiert. – luqui

+0

Kann Löffel auch 'a -> Entweder String a' anstelle von' a -> Maybe a', so dass ich die Nachricht des Fehlers, der gefangen wurde, sehen konnte? Oder wäre das kontraindiziert? –

15

Sie können dies unter Verwendung catch von Control.Exception tun. Beachten Sie jedoch, dass Sie dafür in der IO Monade sein müssen.

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

Ich denke, dass das '$' char in der putStrLn xD entfernt werden könnte. Aber das sollte die akzeptierte Antwort sein, da es sauberer ist – dani24