2010-02-16 20 views
6

Nếu bạn nhìn vào gương cho catches:Cơ chế bảo vệ tùy chỉnh có thể được xác định trong Haskell không?

f = expr `catches` [Handler (\ (ex :: ArithException) -> handleArith ex), 
        Handler (\ (ex :: IOException) -> handleIO ex)] 

Dường như catches đã xác định một cơ chế tùy chỉnh để phù hợp trên các mẫu (hai loại ngoại lệ). Tôi có nhầm lẫn, hoặc điều này có thể được khái quát hóa để cho phép người ta định nghĩa một hàm có thể sử dụng các hàm lambda khớp với một mẫu nhất định không?

Chỉnh sửa: FYI dưới đây là nguồn GHC cho sản phẩm khai thác. Nếu ai đó có thể làm sáng tỏ cách thức hoạt động của nó thì sẽ rất tuyệt.

catches :: IO a -> [Handler a] -> IO a 
catches io handlers = io `catch` catchesHandler handlers 

catchesHandler :: [Handler a] -> SomeException -> IO a 
catchesHandler handlers e = foldr tryHandler (throw e) handlers 
    where tryHandler (Handler handler) res 
       = case fromException e of 
       Just e' -> handler e' 
       Nothing -> res 

Trả lời

5

Đây là tiện ích mở rộng Scoped Type Variables GHC tại nơi làm việc. Theo liên kết để tìm hiểu thêm.

Về cơ bản, bạn xác định xác nhận về loại phải được patter đáp ứng trước khi nó có thể khớp. Vì vậy, yeah, nó giống như bảo vệ, nhưng không hoàn toàn như vậy.

Ví dụ cụ thể này hoạt động như thế nào? Lặn vào sources of "base" library để tìm ra rằng:

class (Show e) => Exception e where 
    toException :: e -> SomeException 
    fromException :: SomeException -> Maybe e 

data SomeException = forall e . Exception e => SomeException e 

instance Exception IOException where 
    toException = IOException 
    fromException (IOException e) = Just e 
    fromException _ = Nothing 

instance Exception ArithException where 
    toException = ArithException 
    fromException (ArithException e) = Just e 
    fromException _ = Nothing 

Chúng ta thấy rằng IOExceptionArithException nhiều loại khác nhau thực hiện typeclass Exception. Chúng tôi cũng thấy rằng toException/fromException là một gói/unwrapping cơ chế cho phép một để chuyển đổi giá trị của loại Exception đến/từ các giá trị của các loại IOException, ArithException vv

Vì vậy, chúng ta có thể đã viết:

f = expr `catches` [Handler handleArith, 
        Handler handleIO] 

handleArith :: ArithException -> IO() 
handleArith ex = .... 

handleIO :: IOException -> IO() 
handleIO ex = .... 

Giả sử rằng IOException xảy ra. Khi catchesHandler xử lý phần tử đầu tiên của danh sách trình xử lý, nó gọi tryHandler, gọi số fromException. Từ định nghĩa của tryHandler nó tuân theo kiểu trả về của fromException phải giống như đối số của handleArith. Mặt khác, e là loại Ngoại lệ, cụ thể là - (IOException ...). Vì vậy, các loại diễn ra theo cách này (đây không phải là một Haskell hợp lệ, nhưng tôi hy vọng rằng bạn sẽ có được quan điểm của tôi):

fromException :: (IOException ...) -> Maybe ArithException 

Từ instance Exception IOException ... nó sau ngay lập tức mà kết quả là Nothing, vì vậy xử lý này được bỏ qua . Bởi cùng một lý do, trình xử lý sau sẽ được gọi, bởi vì fromException sẽ trả về (Just (IOException ...)).

Vì vậy, bạn đã sử dụng chữ ký loại handleArithhandleIO để chỉ định khi nào mỗi người trong số họ sẽ được gọi và fromException/toException đảm bảo rằng nó đã xảy ra theo cách này.

Nếu muốn, bạn cũng có thể hạn chế các loại handleIOhandleArith bên trong định nghĩa f, sử dụng các biến kiểu phạm vi. Có thể cho rằng, điều này có thể giúp bạn dễ đọc hơn.

Hoàn tất, Biến loại phạm vi không phải là người chơi chính ở đây. Chúng chỉ được sử dụng để thuận tiện. Máy móc chính để chơi loại thủ thuật này là fromException/toException và bạn bè. Biến kiểu Scoped chỉ cho phép bạn có cú pháp gần giống với các mẫu bảo vệ.

+0

Biến kiểu phạm vi có liên quan như thế nào? Tôi không thấy cơ chế này tại nơi làm việc trong nguồn bắt. – me2

+0

Được cập nhật để giải thích từ thủ thuật ngoại lệ/ngoại lệ – ADEpt

1
case() of 
()| foo expr1 -> handleFooCase 
    | bar expr2 -> handleBarCase 
    | otherwise -> blah 
Các vấn đề liên quan