2012-05-14 6 views
12

Czytam RWH, i doszedłem do rozdziału 9. Wprowadza się następujący fragment kodu:Funkcja „rączka” i Real World Haskell

import System.IO 
import Control.Exception 

saferFileSize :: FilePath -> IO (Maybe Integer) 
saferFileSize path = handle (\_ -> return Nothing) $ do 
    h <- openFile path ReadMode 
    size <- hFileSize h 
    hClose h 
    return (Just size) 

To nie będzie jednak skompilować, dając następujący komunikat o błędzie:

test.hs:5:22: 
    Ambiguous type variable `e0' in the constraint: 
     (Exception e0) arising from a use of `handle' 
    Probable fix: add a type signature that fixes these type variable(s) 
    In the expression: handle (\ _ -> return Nothing) 
    In the expression: 
     handle (\ _ -> return Nothing) 
     $ do { h <- openFile path ReadMode; 
      size <- hFileSize h; 
      hClose h; 
      return (Just size) } 
    In an equation for `saferFileSize': 
     saferFileSize path 
      = handle (\ _ -> return Nothing) 
      $ do { h <- openFile path ReadMode; 
        size <- hFileSize h; 
        hClose h; 
        .... } 

Co tu jest nie tak? Dlaczego nie skompiluje?

Odpowiedz

25

Niedługo po RWH wyszedł, interfejs wyjątek został zmieniony w celu wspierania bardziej elastycznych procedur obsługi Jeżeli typ obsługi określa, które wyjątki będą przechwytywać. Na przykład. program obsługujący, który pobiera SomeException będzie przechwytywać cokolwiek (zazwyczaj nie jest to dobry pomysł), podczas gdy program obsługujący, który pobiera IOException, będzie przechwytywał tylko wyjątki IO.

W związku z tym łatwo jest spotkać się z problemami z niejednoznacznością przy pomocy programów do obsługi "do niczego", takich jak ten w twoim przykładzie, ponieważ kompilator nie może wywnioskować, jaki typ wyjątków próbujesz przechwycić. Łatwym sposobem naprawy jest podanie podpisu typu dla funkcji obsługi.

handle ((\_ -> return Nothing) :: IOException -> IO (Maybe Integer)) $ do ... 

Chociaż może to być dość szczegółowe. Alternatywnym rozwiązaniem jest specjalizacja handle.

handleIO :: (IOException -> IO a) -> IO a -> IO a 
handleIO = handle 

Następnie można po prostu użyć handleIO kiedy chcesz obsługiwać wyjątki IO, bez konieczności przeliterować podpis rodzaj przewodnika.

saferFileSize path = handleIO (\_ -> return Nothing) $ do ... 

Trzecią opcją jest użycie rozszerzenie ScopedTypeVariables, które (między innymi) pozwala na zapewnienie typu adnotacji tylko dla argumentu funkcji, dzięki czemu reszta należy wnioskować.

{-# LANGUAGE ScopedTypeVariables #-} 
saferFileSize path = handle (\(_ :: IOException) -> return Nothing) $ do ... 
+0

Dokumentacja dla 'handle' funkcji na stronie Haskell jest bardzo jasne, o tym (przynajmniej do poziomu wejścia ludzi - tych, którzy potrzebują dokumentacji) https://wiki.haskell.org/Exception Dzięki za bardzo jasne wyjaśnienie, że kompilator potrzebuje nas tylko do określenia typów wyjątków do obsługi! – jocull

4

RWH jest dość stary. Sygnatura funkcji handle została zmieniona w GHC 6.10 lub podobnym. Aby uzyskać dostęp do starej wersji, należy zaimportować Control.OldException zamiast Control.Exception ". Otrzymasz ostrzeżenia o wycofaniu, ale program się skompiluje.

Albo można użyć nowego interfejsu i dać Handler wyraźny podpis, tak:

((\ _ -> return Nothing) :: IOException -> IO (Maybe Integer))