Ошибка переменной типа неоднозначного msg - PullRequest
21 голосов
/ 10 января 2009

Я не думаю, что это ошибка, но я немного озадачен, почему это не работает. Дополнительный вопрос: почему в нем упоминается переменная e? Там нет переменной е.

    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> 

Видимо, в ghci 6.8 он работает нормально, я использую 6.10.1.

Редактировать: я свернул код. Я ожидаю, что будет иметь одинаковый результат в 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

пытается скомпилировать:

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

Ответы [ 5 ]

11 голосов
/ 07 сентября 2012

Тип Control.Exception.handle:

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

Проблема, которую вы видите, состоит в том, что лямбда-выражение (\_ -> return "err") не относится к типу e -> IO a, где e является экземпляром Exception. Ясно как грязь? Хорошо. Теперь я предоставлю решение, которое на самом деле должно быть полезным:)

В вашем случае просто так получается, что e должно быть Control.Exception.ErrorCall, поскольку undefined использует error, который выбрасывает ErrorCall (экземпляр Exception).

Для обработки использования undefined вы можете определить что-то вроде handleError:

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

По сути, это псевдоним Control.Exception.handle с e, зафиксированным как ErrorCall, который error выбрасывает.

Выглядит так при запуске в GHCi 7.4.1 :

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

Для обработки всех исключений функцию handleAll можно записать следующим образом:

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

Перехват всех исключений имеет последствия, хорошо описанные в этом отрывке документации Control.Exception:

Перехват всех исключений

Можно перехватить все исключения, используя тип SomeException:

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

ОДНАКО, это обычно не то, что вы хотите сделать!

Например, предположим, что вы хотите прочитать файл, но если он не существует, продолжайте, как если бы он содержал "". Может возникнуть соблазн просто перехватить все исключения и вернуть "" в обработчик. Однако это имеет всевозможные нежелательные последствия. Например, если пользователь нажимает control-C как раз в нужный момент, то будет зафиксировано исключение UserInterrupt, и программа продолжит работу, полагая, что файл содержит "". Аналогично, если другой поток попытается уничтожить поток, читающий файл, исключение ThreadKilled будет проигнорировано.

Вместо этого вы должны ловить только те исключения, которые вам действительно нужны. В этом случае это, вероятно, будет более конкретным, чем даже «любое исключение ввода-вывода»; ошибка разрешений, вероятно, также будет обрабатываться по-другому. Вместо этого вы, вероятно, захотите что-то вроде:

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

Бывают случаи, когда вам действительно нужно поймать какое-либо исключение. Тем не менее, в большинстве случаев это просто так, вы можете сделать некоторые очистки; вы на самом деле не заинтересованы в самом исключении. Например, если вы открываете файл, то хотите снова его закрыть, независимо от того, выполняется ли обработка файла нормально или выдает исключение. Однако в этих случаях вы можете использовать такие функции, как bracket, finally и onException, которые фактически никогда не передают вам исключение, а просто вызывают функции очистки в соответствующих точках.

Но иногда вам действительно нужно перехватить любое исключение и посмотреть, что это за исключение. Один пример находится на самом верхнем уровне программы, вы можете перехватить любое исключение, распечатать его в лог-файл или на экран, а затем завершить работу корректно. В этих случаях вы можете использовать catch (или одну из других функций перехвата исключений) с типом SomeException.

Источник: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception.html#g:4

10 голосов
/ 10 января 2009

Эта проблема появляется только в GHC 6.10; его нельзя скопировать в GHC 6.8, потому что тип handle отличается:

: nr@homedog 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> 

ОК, может быть, я наконец смогу сделать это правильно. Я думаю, что проблема заключается в не ограничении мономорфизма, а скорее в том, что вы столкнулись с проблемой чтения / показа: вы предлагаете обработать какой-то тип исключения в новой версии `handle, существует более одного типа исключения, и тип этого исключения не отображается в вашем результате. Таким образом, у компилятора нет способа узнать , какой тип исключения вы пытаетесь обработать. Один из способов сделать это - выбрать один. Вот код, который работает:

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

Кстати, пример использования handle в документации библиотеки GHC не компилируется в 6.10. Я подал отчет об ошибке.

3 голосов
/ 24 мая 2009

Обходной путь должен использовать Control.OldException в ghc 6.10. * Вместо Control.Exception.

2 голосов
/ 26 мая 2009

Попробуйте дать вашему обработчику тип SomeException -> IO x, где x - конкретный тип, например

import Control.Exception
let f _ = putStrLn "error" :: SomeException -> IO () 
in handle f undefined 
1 голос
/ 10 января 2009

«Исключение е», вероятно, происходит из сигнатуры типа «handle».

Документация говорит:

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

В GHC 6.8 раньше все было иначе, что объясняет, почему я не получаю эту ошибку.

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

Кажется, вы столкнулись с ограничением мономорфизма. Этот "_" - шаблон должен быть мономорфным (что и есть в ghc 6.8) или явно напечатан. «Обходной путь» - поместить шаблон в левой части определения, где он представляет собой «простую привязку шаблона», как указано в отчете Haskell.

Попробуйте это:

let f _ = return "err"
handle f undefined

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

...