2009-01-10 14 views
21

Nie sądzę, że to błąd, ale jestem nieco zdziwiony, dlaczego to nie działa. Pytanie dodatkowe brzmi: dlaczego wspomina zmienną e? Nie ma zmiennej e.Niejednoznaczny błąd zmiennej typu msg

 
    Prelude> :m +Control.Exception 
    Prelude Control.Exception> handle (\_-> return "err") undefined 

    <interactive>:1:0: 
     Ambiguous type variable `e' in the constraint: 
      `Exception e' 
      arising from a use of `handle' at <interactive>:1:0-35 
     Probable fix: add a type signature that fixes these type variable(s) 
    Prelude Control.Exception> 

Najwyraźniej działa dobrze w ghci 6.8, używam 6.10.1.

Edytuj: Zminimalizowałem kod. Spodziewam się, że mają ten sam rezultat zarówno 6.8, 6.10

class C a                          

foo :: C a => (a -> Int)-> Int                     
foo _ = 1                          

arg :: C a => a -> Int                       
arg _ = 2                          

bar :: Int                          
bar = foo arg 

próbuje go skompilować:

 
[1 of 1] Compiling Main    (/tmp/foo.hs, interpreted) 

/tmp/foo.hs:12:10: 
    Ambiguous type variable `a' in the constraint: 
     `C a' arising from a use of `arg' at /tmp/foo.hs:12:10-12 
    Probable fix: add a type signature that fixes these type variable(s) 
Failed, modules loaded: none. 
Prelude Control.Exception> 

Odpowiedz

10

Ten problem pojawia się tylko w GHC 6.10; to nie może być powielana w GHC 6,8 ponieważ typ handle jest inna:

: [email protected] 620 ; ghci 
GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help 
Loading package base ... linking ... done. 
Prelude> :m +Control.Exception 
Prelude Control.Exception> handle (\_ -> return "err") undefined 
"err" 
Prelude Control.Exception> 

OK może uda mi się uzyskać to prawo w końcu. Wydaje mi się, że problemem jest ograniczenie monomorfizmu, ale raczej trafiłeś w instancję problemu z odczytem/prezentacją: oferujesz obsługę pewnego rodzaju wyjątku, w nowej wersji `uchwytów, jest więcej niż jeden typ wyjątku, a typ tego wyjątku nie pojawia się w wyniku. Tak więc kompilator nie ma możliwości poznania typu wyjątku, który próbujesz obsłużyć. Jednym ze sposobów działania jest wybranie jednego. Oto niektóre kod, który działa:

Prelude Control.Exception> let alwaysError :: SomeException -> IO String; alwaysError = \_ -> return "err" 
Prelude Control.Exception> handle alwaysError undefined 
"err" 

Nawiasem mówiąc, przykład wykorzystanie handle w dokumentacji biblioteki GHC nie skompilować pod 6.10. Złożyłem zgłoszenie błędu.

+0

Dlaczego kompilator trzeba wiedzieć, które wyjątek? Funkcja obsługuje każdy typ z klasy. – luntain

+0

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'.) –

+0

Jeśli używasz rozszerzenia ScopedTypeVariables, możesz po prostu zrobić "handle (\ (_ (SomeException) -> return" err ") undefined". – porges

1

"Wyjątek e" prawdopodobnie wynika z podpisu typu "uchwyt".

The documentation mówi:

handle :: Exception e => (e -> IO a) -> IO a -> IO a 

W GHC 6,8 kiedyś być różne, co tłumaczyłoby, dlaczego nie dostać ten błąd.

handle :: (Exception -> IO a) -> IO a -> IO a 

Wygląda na to, że napotykasz ograniczenia dotyczące monomorfizmu. Ten "_" - Wzór musi być monomorficzny (który jest z ghc 6.8) lub jawnie wpisany. "Obejście" polega na umieszczeniu wzoru po lewej stronie definicji, gdzie stanowi "proste powiązanie wzoru" określone przez raport Haskella.

Spróbuj tego:

let f _ = return "err" 
handle f undefined 

http://www.haskell.org/haskellwiki/Monomorphism_restriction

+0

Niech f _ = return «Err» uchwyt f niezdefiniowanej daje ten sam błąd – luntain

+0

jak o pragma {- # -XNoMonomorphismRestriction # -} – Waquo

+0

Myślę, że to {- # LANGUAGE NoMonomorphismRestriction # -} – Waquo

3

Rozwiązaniem jest użycie Control.OldException w ghc 6.10 * zamiast Control.Exception..

2

Spróbuj podać swojemu handlerowi typ SomeException -> IO x, gdzie x jest typem konkretnym, np.

import Control.Exception 
let f _ = putStrLn "error" :: SomeException -> IO() 
in handle f undefined 
11

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.ErrorCalle 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

Powiązane problemy