16

Sono sorpreso che non ho trovato una risposta a questo ovunque.Come prendere (e ignorare) una chiamata alla funzione di errore?

Sto scrivendo un roguelike e sto usando la libreria ncurses da hackage, che è un buon wrapper attorno alla libreria ncurses. Ora ncurses ha questa stranezza in cui se provi a scrivere il carattere in basso a destra, lo fa, quindi prova a spostare il cursore sul carattere successivo, poi fallisce perché non c'è nessun posto dove spostarlo. Restituisce un valore di errore che puoi solo ignorare.

Il mio problema è che lo scrittore di librerie haskell ncurses controlla diligentemente eventuali errori su tutte le chiamate e, quando ce n'è uno, chiama: errore "drawText: etc etc.".

In altri linguaggi, come c o python, per aggirare questo si è costretti a ignorare l'errore o catturare e ignorare l'eccezione, ma per la vita di me non riesco a capire come farlo in haskell. La funzione di errore è irrecuperabile?

Modificheremo la libreria localmente per non controllare gli errori su quella funzione se necessario, ma odio farlo. Sono anche aperto a qualsiasi soluzione che mi consenta di disegnare l'ultimo carattere senza spostare il cursore, ma non penso che sia possibile.

+0

Hoogle per "prendere" [1]. Secondo link in basso. [1] http://haskell.org/hoogle/?hoogle=catch –

+0

Sfortunatamente l'errore in questione non è nella monade IO. Beh, inizia in IO, quindi vai a runCurses, che è la monade Maledizioni, quindi updateWindow, che è la monade di aggiornamento. Quindi non credo che la risposta di Paul funzionerà. Ma l'aspetto di luqui ha un potenziale e lo proverò quando torno a casa. –

+0

guardando oltre le ncurse Ho la sensazione che non funzionerà. 'drawText' non chiama' error', ma delega direttamente a una funzione C. E ritorna nel monod di 'Update', che è 'Finestra ReaderT IO a' =' Finestra -> IO a', quindi 'unsafeCleanup' funzionerà solo se si verifica un errore durante la generazione di quella * funzione *, non quando eseguire l'azione (improbabile). Penso che le tue opzioni siano: catturare l'errore in IO al livello più alto, o aprire l'origine curses per farti iniettare una funzione 'catch' più locale. (Può essere fatto facilmente, rompe l'incapsulamento) – luqui

risposta

12

error deve essere osservabile come un loop infinito. Puoi solo prendere error in IO, che è come dire "sì, puoi se conosci la magia". Ma dalla parte davvero bella di Haskell, puro codice, non è recuperabile, e quindi è fortemente consigliato non da utilizzare nel codice, solo quanto si userebbe mai un ciclo infinito come codice di errore.

ncurses è maleducato e ti fa fare magie per correggerlo. Direi che lo unsafePerformIO sarebbe garantito per ripulirlo. Oltre a questo, questo è in gran parte lo stesso della risposta di Paolo.

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) 

Poi avvolgere unsafeCleanup intorno a qualsiasi valore che sarebbe valutato come un errore per trasformarlo in un Maybe.

+0

btw, 'Exc.il gestore catch (Exc.evaluate (Just $! x)) sarebbe leggermente più pulito imho – hvr

+1

FWIW questo è ora implementato nella libreria [spoon] (http://hackage.haskell.org/package/spoon). – luqui

+0

Can spoon fa anche 'a -> Either String a' invece di' a -> Forse a' in modo che io possa vedere il messaggio dell'errore che è stato catturato? O sarebbe controindicato? –

15

È possibile farlo utilizzando catch da Control.Exception. Notare, tuttavia, che è necessario essere nella monade IO per farlo.

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

Penso che il carattere '$' nel putStrLn possa essere rimosso xD. Ma questa dovrebbe essere la risposta accettata, in quanto è più pulito – dani24