2015-07-05 7 views
5

Sto giocando con le eccezioni in haskell e sono incappato in una cosa che non riesco ancora a capire.Gestione delle eccezioni generate da codice puro con `try`

In GHCi faccio:

Prelude Control.Exception> let thrower = (read "A") :: Int 
Prelude Control.Exception> :{ 
Prelude Control.Exception| let main = do 
Prelude Control.Exception|  x <- (try $ return thrower) :: IO (Either SomeException Int) 
Prelude Control.Exception|  print x 
Prelude Control.Exception| :} 
Prelude Control.Exception> main 

Questo definisce thrower, la mia espressione di test che non riuscirà ad eccezione.

Poi definiscono main che avvolge tale espressione in try (avvolgendolo in IO prima, poiché try accetta IO) e quindi scarta da IO (prodotto da try) e print s esso.

Tutto sembra grande finora - valutare main in repl restituisce eccezione avvolti in entrambi i casi:

Right *** Exception: Prelude.read: no parse 

Tuttavia, se provo a compilare ed eseguire lo stesso codice come un app:

module Main where 

import Control.Exception 

thrower = (read "A") :: Int 

main = do 
    x <- (try $ return thrower) :: IO (Either SomeException Int) 
    print x 

... si blocca con eccezione:

haskelltest.exe: Prelude.read: no parse 

Sembra li eccezione ke scivolò oltre try.

Cosa mi manca qui e qual è il modo corretto di gestirlo?

risposta

9

Beh, in pratica (as Sebastian Redl pointed out earlier) questo è un problema di rigidità. return thrower non valuta in alcun modo thrower, quindi trysupera. Solo quando viene stampato il contenuto di Either SomeException Int, ovvero Right thrower, lo read tenta effettivamente di analizzare "A" e non riesce ... ma a questo punto, lo try è già terminato.

Il modo per evitare questo è di inject the parse result strictly nella IO Monade, con

main = do 
    x <- try $ evaluate thrower :: IO (Either SomeException Int) 
    print x 

Perché la try non riesce con il codice in GHCi Non lo so; Suppongo che non dovrebbe. Aha: as Reid noted, in realtà non funziona correttamente!

Probabilmente, questo è un esempio del motivo per cui le eccezioni dovrebbero essere generalmente evitate in Haskell. Use a suitable monad transformer per rendere esplicito quali errori potrebbero verificarsi e per ottenere una valutazione affidabile del controllo degli errori.

+0

Come nota a margine per chiunque abbia una situazione simile, nella mia app effettiva non è stato sufficiente "valutare" l'espressione in WHNF e ho dovuto usare anche 'force' (http://hackage.haskell.org /packages/archive/deepseq/latest/doc/html/Control-DeepSeq.html#v:force) –

+0

@EugenyLoy: poi di nuovo, come ho detto, probabilmente non dovresti usare le eccezioni. Con un errore corretto monad l'intero problema non si pone in primo luogo, e il risultato viene valutato esattamente nel modo più approfondito necessario per assicurarsi che non ci siano errori. – leftaroundabout

7

Parte di ciò che ti manca è che quando hai eseguito il codice in ghci, try non ha rilevato l'errore generato da read "A" :: Int. Non ti aspettavi un risultato Left <something>? Vedi la risposta di leftaroundabout per il motivo.

La differenza tra ghci e ghc qui è probabilmente dovuta al buffering dell'output: l'output su stdout (come "Right " qui) è unbuffered in ghci, ma la linea è bufferizzata di default in un programma compilato.

+0

Ottimo punto. Non ho notato il 'Right', e ho pensato che fosse un' Left'. – chi

+0

Nemmeno io, ben individuato. – leftaroundabout