Rodzaj Control.Exception.handle
jest:
handle :: Exception e => (e -> IO a) -> IO a -> IO a
Problem widzisz to, że wyrażenie lambda (\_ -> return "err")
nie jest typu e -> IO a
gdzie e
jest instancją Exception
. Czyste jak błoto? Dobry. Teraz będę dostarczać rozwiązania, które faktycznie powinny być przydatne :)
Tak się składa, że w Twoim przypadku powinno być Control.Exception.ErrorCall
e
od undefined
wykorzystuje error
który rzuca ErrorCall
(instancję Exception
).
Aby obsłużyć zastosowań undefined
można zdefiniować coś jak handleError
:
handleError :: (ErrorCall -> IO a) -> IO a -> IO a
handleError = handle
To w istocie aliasem Control.Exception.handle
z e
ustalona jako ErrorCall
która jest co error
rzutów.
Wygląda na to, kiedy uruchomiony w GHCi 7.4.1:
ghci> handleError (\_ -> return "err") undefined
"err"
Aby obsłużyć wszystkie wyjątki handleAll
funkcji można zapisać następująco:
handleAll :: (SomeException -> IO a) -> IO a -> IO a
handleAll = handle
Łapanie wszystkich wyjątków ma konsekwencje dobrze opisane w tym fragmencie dokumentacji Control.Exception
:
Catching wszystkie wyjątki
Jest możliwe, aby złapać wszystkie wyjątki, stosując typ SomeException
:
catch f (\e -> ... (e :: SomeException) ...)
Jednak to nie jest zwykle to, co chcesz zrobić!
Załóżmy na przykład, że chcesz odczytać plik, ale jeśli nie istnieje, kontynuuj, jakby zawierał ""
. Możesz ulec pokusie, aby po prostu wychwycić wszystkie wyjątki i zwrócić ""
w programie obsługi. Ma to jednak wiele niepożądanych konsekwencji. Na przykład, jeśli użytkownik naciśnie control-C w odpowiednim momencie, wówczas zostanie przechwycony wyjątek UserInterrupt
, a program będzie nadal działał pod nadzorem, że plik zawiera ""
. Podobnie, jeśli inny wątek próbuje zabić wątek czytający plik, wyjątek ThreadKilled
zostanie zignorowany.
Zamiast tego należy chwytać dokładnie te wyjątki, które naprawdę chcesz. W tym przypadku będzie to prawdopodobnie bardziej szczegółowe niż "dowolny wyjątek IO"; błąd uprawnień prawdopodobnie również chciałby być obsługiwany inaczej. Zamiast tego prawdopodobnie chcesz coś takiego:
e <- tryJust (guard . isDoesNotExistError) (readFile f)
let str = either (const "") id e
Są okazje, gdy naprawdę trzeba złapać jakiegokolwiek wyjątku. Jednak w większości przypadków jest to po prostu możliwe, abyś mógł posprzątać; w rzeczywistości nie jesteś zainteresowany samym wyjątkiem.Na przykład, jeśli otworzysz plik, to chcesz go ponownie zamknąć, czy przetwarzanie pliku zostanie wykonane normalnie, czy zgłasza wyjątek. Jednak w takich przypadkach można używać funkcji, takich jak bracket
, i onException
, które nigdy nie przekazują wyjątku, ale po prostu wywołaj funkcje czyszczenia w odpowiednich punktach.
Ale czasami naprawdę musisz złapać jakiś wyjątek i zobaczyć, co to jest wyjątek. Jeden przykład znajduje się na najwyższym poziomie programu, możesz chcieć złapać dowolny wyjątek, wydrukować go do pliku dziennika lub ekranu, a następnie wyjść z wdziękiem. W takich przypadkach można użyć catch
(lub jednej z innych funkcji wychwytywania wyjątków) o typie SomeException
.
Źródło: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception.html#g:4
Dlaczego kompilator trzeba wiedzieć, które wyjątek? Funkcja obsługuje każdy typ z klasy. – luntain
Ze względu na typ "uchwytu"; każde użycie 'handle' musi być zastosowane do * one * określonego typu z klasy Exception. Ponieważ twój program obsługi działa dla wszystkich typów w klasie, kompilator nie może przypisać typu do 'handle'. ('' 'Pochodzi od typu' handle'.) –
Jeśli używasz rozszerzenia ScopedTypeVariables, możesz po prostu zrobić "handle (\ (_ (SomeException) -> return" err ") undefined". – porges