2012-05-14 25 views
12

Tôi đang đọc RWH, và tôi đã đến với chương 9. Nó giới thiệu các đoạn mã sau:Các "xử lý" chức năng và 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) 

Nó sẽ không biên dịch tuy nhiên, cho thông báo lỗi sau:

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; 
        .... } 

Điều gì xảy ra ở đây? Tại sao nó không biên dịch?

Trả lời

25

Không quá lâu sau khi RWH ra, giao diện ngoại lệ đã được thay đổi để hỗ trợ xử lý linh hoạt hơn nơi các loại của trình xử lý xác định ngoại lệ nào nó sẽ bắt. Ví dụ. một handler mất SomeException sẽ bắt bất cứ điều gì (thường không phải là một ý tưởng tốt), trong khi một handler mà mất IOException sẽ chỉ bắt IO ngoại lệ. Kết quả của việc này là dễ dàng chạy vào các vấn đề mơ hồ với các trình xử lý "không có gì" như trong ví dụ của bạn, vì trình biên dịch không thể suy ra loại ngoại lệ bạn đang cố bắt giữ. Một cách dễ dàng để sửa lỗi này là cung cấp một chữ ký kiểu cho hàm xử lý của bạn.

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

Mặc dù, điều này có thể hơi tiết. Một giải pháp thay thế là chuyên về handle.

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

Sau đó, bạn chỉ có thể sử dụng bất cứ khi nào handleIO bạn muốn xử lý ngoại lệ IO, mà không cần phải giải thích rõ ràng các loại chữ ký của người điều khiển.

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

Một lựa chọn thứ ba là sử dụng phần mở rộng ScopedTypeVariables, mà (trong số những thứ khác) cho phép bạn để cung cấp một loại chú thích cho chỉ là đối số của một hàm, cho phép các phần còn lại để được suy ra.

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

Tài liệu về chức năng 'xử lý' trên trang web Haskell không rõ ràng về điều này (ít nhất là đối với những người ở cấp nhập cảnh - những người cần tài liệu!) Https://wiki.haskell.org/Exception Cảm ơn bạn đã giải thích rất rõ ràng rằng trình biên dịch chỉ cần chúng tôi xác định các loại ngoại lệ để xử lý! – jocull

4

RWH khá cũ. Chữ ký chức năng handle đã thay đổi trong GHC 6.10 trở lên.

Để sử dụng phiên bản cũ, hãy nhập Control.OldException thay vì Control.Exception`. Bạn sẽ nhận được cảnh báo không dùng nữa nhưng chương trình sẽ biên dịch.

Hoặc bạn có thể sử dụng giao diện mới và đưa ra xử lý một chữ ký rõ ràng, như vậy:

((\ _ -> return Nothing) :: IOException -> IO (Maybe Integer)) 
Các vấn đề liên quan