2009-01-10 17 views
21

Ich glaube nicht, dass es ein Fehler ist, aber ich bin ein wenig verwirrt, warum das nicht funktioniert. Eine Bonusfrage ist, warum erwähnt es Variable e? Es gibt keine Variable e.Mehrdeutiger Typ variabler Fehler 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> 

Anscheinend funktioniert es gut in ghci 6.8, verwende ich 6.10.1.

Edit: Ich habe den Code minimiert. Ich gehe davon aus, dass das gleiche Ergebnis haben sowohl in 6,8 und 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 

versucht, es zu kompilieren:

 
[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> 

Antwort

10

Dieses Problem zeigt sich nur in GHC 6.10; es kann nicht in GHC 6.8, weil die Art der handle anders dupliziert werden:

: [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 vielleicht kann ich dieses Recht endlich bekommen. Ich denke, das Problem ist nicht die Monomorphie Einschränkung, sondern Sie haben eine Instanz des Read/Show-Problems getroffen: Sie bieten an, irgendeine Art von Ausnahme zu behandeln, in der neuen Version von `handle, gibt es mehr als eine Art von Ausnahme, und der Typ dieser Ausnahme erscheint nicht in Ihrem Ergebnis. Also der Compiler hat keine Möglichkeit zu wissen, welche Art der Ausnahme, die Sie versuchen zu behandeln. Eine Möglichkeit, dies zu bewerkstelligen, besteht darin, einen auszuwählen. Hier ist ein Code, der funktioniert:

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

übrigens das Beispiel für die Verwendung von handle in der GHC Bibliothek Dokumentation nicht unter 6,10 nicht kompiliert. Ich habe einen Fehlerbericht eingereicht.

+0

Warum der Compiler wissen müssen die Ausnahme? Die Funktion behandelt jeden Typ aus der Klasse. – luntain

+0

Wegen der Art von 'handle'; Jede Verwendung von 'handle' muss auf einen bestimmten Typ aus der Exception-Klasse angewendet werden. Da Ihr Handler für alle Typen in der Klasse arbeitet, gibt es keine Möglichkeit für den Compiler, dem Handle einen Typ zuzuweisen. (Das 'e' kommt vom Typ von' handle'.) –

+0

Wenn Sie die ScopedTypeVariables-Erweiterung verwenden, können Sie einfach 'handle (\ (_ :: SomeException) -> return "err") undefined'. – porges

1

"Ausnahme e" ist wahrscheinlich von der Typ-Signatur von "handle".

The documentation sagt:

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

In GHC 6.8 es verwendet, anders zu sein, was erklären würde, warum ich nicht, dass Fehler.

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

Scheint, dass Sie in die Monomorphie-Beschränkung laufen. Das "_" -Muster muss monomorph sein (was mit ghc 6.8 ist) oder explizit typisiert sein. Eine "Problemumgehung" besteht darin, das Muster auf die linke Seite einer Definition zu setzen, wo es eine "einfache Musterbindung" darstellt, wie im Haskell-Bericht angegeben.

Versuchen Sie folgendes:

let f _ = return "err" 
handle f undefined 

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

+0

let f _ = return ‚err‘ Griff f undefined gibt den gleichen Fehler – luntain

+0

Wie wäre es die Pragma {- # -XNoMonomorphismRestriction # -} – Waquo

+0

Ich denke, es ist {- # SPRACHE NoMonomorphismRestriction # -} – Waquo

3

Eine Abhilfe Control.OldException in ghc 6.10 verwenden * statt Control.Exception..

2

Versuchen Sie, Ihrem Handler den Typ SomeException -> IO x zu geben, wobei x ein konkreter Typ ist, z.

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

Die Art der Control.Exception.handle ist:

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

Das Problem, das Sie sehen, ist, dass der Lambda-Ausdruck (\_ -> return "err") nicht vom Typ ist e -> IO a wo e eine Instanz von Exception ist. Klar wie Schlamm? Gut. Jetzt werde ich eine Lösung bereitzustellen, die tatsächlich nützlich sein sollte :)

Es ist einfach so in Ihrem Fall kommt vor, dass eControl.Exception.ErrorCall sein sollte, da undefined verwendet error die ErrorCall (eine Instanz von Exception) wirft.

Verwendungen undefined behandeln Sie so etwas wie handleError definieren:

handleError :: (ErrorCall -> IO a) -> IO a -> IO a 
handleError = handle 

Es ist im Wesentlichen ein Alias ​​Control.Exception.handle mit e fixiert, wie ErrorCall das ist, was error wirft.

Es sieht aus wie diese ausgeführt werden, wenn in GHCi 7.4.1:

ghci> handleError (\_ -> return "err") undefined 
"err" 

Um alle Ausnahmen eine handleAll Funktion behandeln kann geschrieben werden wie folgt:

handleAll :: (SomeException -> IO a) -> IO a -> IO a 
handleAll = handle 

alle Ausnahmen Beschäftigten hat Folgen beschrieben gut in diesem Auszug der Control.Exception Dokumentation:

Fangen alle Ausnahmen

Es ist möglich, alle Ausnahmen zu fangen, indem sie die Art SomeException mit:

catch f (\e -> ... (e :: SomeException) ...) 

Dies ist jedoch normalerweise nicht das, was Sie tun möchten!

Angenommen, Sie möchten eine Datei lesen, aber wenn sie nicht existiert, fahren Sie fort, als ob sie "" enthielte. Sie könnten versucht sein, alle Ausnahmen abzufangen und "" im Handler zurückgeben. Dies hat jedoch alle möglichen unerwünschten Folgen. Wenn der Benutzer beispielsweise control-C genau zum richtigen Zeitpunkt drückt, wird die Ausnahme UserInterrupt abgefangen, und das Programm wird unter der Annahme fortgesetzt, dass die Datei "" enthält. Wenn ein anderer Thread versucht, den Thread zu beenden, der die Datei liest, wird die Ausnahme ThreadKilled ebenfalls ignoriert.

Stattdessen sollten Sie nur genau die Ausnahmen abfangen, die Sie wirklich wollen. In diesem Fall wäre dies wahrscheinlich spezifischer als selbst "irgendeine IO-Ausnahme"; Ein Berechtigungsfehler würde wahrscheinlich auch anders behandelt werden. Stattdessen würden Sie wahrscheinlich so etwas wie wollen:

e <- tryJust (guard . isDoesNotExistError) (readFile f) 
let str = either (const "") id e 

Es gibt Gelegenheiten, wenn Sie wirklich jede Art von Ausnahme brauchen zu fangen. In den meisten Fällen ist dies jedoch nur so, dass Sie etwas aufräumen können; Sie sind nicht wirklich an der Ausnahme selbst interessiert.Wenn Sie beispielsweise eine Datei öffnen, möchten Sie sie erneut schließen, unabhängig davon, ob die Verarbeitung der Datei normal ausgeführt wird oder eine Ausnahme auslöst. In diesen Fällen können Sie jedoch Funktionen wie bracket, finally und onException verwenden, die Ihnen niemals die Ausnahme übergeben, sondern nur die Bereinigungsfunktionen an den entsprechenden Stellen aufrufen.

Aber manchmal müssen Sie wirklich irgendeine Ausnahme fangen, und tatsächlich sehen, was die Ausnahme ist. Ein Beispiel ist auf der obersten Ebene eines Programms, möchten Sie möglicherweise eine Ausnahme abfangen, drucken Sie es in eine Protokolldatei oder den Bildschirm, und dann ordnungsgemäß beendet. In diesen Fällen können Sie catch (oder eine der anderen ausnahmebedingten Funktionen) mit dem Typ SomeException verwenden.

Quelle: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception.html#g:4