2011-12-19 39 views
5

Sau một haskell tutorial, tác giả cung cấp việc thực hiện sau đây của withFile phương pháp:Làm thế nào withFile được thực hiện trong Haskell

withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a 
withFile' path mode f = do 
    handle <- openFile path mode 
    result <- f handle 
    hClose handle 
    return result 

Nhưng tại sao chúng ta cần phải quấn result trong một return? Không phải chức năng được cung cấp f đã trả lại số IO có thể được xem bằng loại Handle -> IO a?

Trả lời

7

Bạn nói đúng: f đã trả về một IO, vì vậy nếu các chức năng được viết như thế này:

withFile' path mode f = do 
    handle <- openFile path mode 
    f handle 

sẽ không có nhu cầu cho một sự trở lại. Vấn đề là hClose handle do thỏa thuận giữa, vì vậy chúng ta phải lưu trữ kết quả đầu tiên:

result <- f handle 

và làm <- được thoát khỏi những IO. Vì vậy, return đặt nó trở lại.

+0

Oh d'oh! Hoàn toàn bỏ lỡ toán tử _sucking_ '<-'! – drozzy

+1

Có thể nó cũng là 'let result = f handle; hClose xử lý; result' hoặc tôi đã không đọc lại một lần nữa? – delnan

+3

@delnan sẽ là đường dẫn chế độ 'do {handle <- openFile; hClose xử lý; f xử lý; } ', do đó,' f handle' có lẽ sẽ phàn nàn về một chốt đóng. –

3

Đây là một trong những điều nhỏ nhặt phức tạp khiến tôi bối rối khi lần đầu tiên tôi thử Haskell. Bạn hiểu nhầm ý nghĩa của cấu trúc <- trong ký hiệu. result <- f handle không có nghĩa là "chỉ định giá trị của f handle đến result"; nó có nghĩa là "liên kết result với giá trị 'được trích xuất' từ giá trị monome của f handle" (trong đó 'trích xuất' xảy ra theo cách nào đó được xác định bằng cá thể Monad cụ thể mà bạn đang sử dụng, trong trường hợp này là đơn nguyên IO).

Tức là, đối với một số đơn nguyên typeclass m, báo cáo kết quả <- mất một biểu hiện của loại m a ở phía bên tay phải và một biến kiểu a ở phía bên tay trái, và liên kết với các biến đến một giá trị. Do đó, trong ví dụ cụ thể của bạn, với result <- f handle, chúng tôi có các loại f result :: IO a, result :: areturn result :: IO a.

Ghi chú PS cũng có dạng đặc biệt là let (không có từ khóa in trong trường hợp này!) Hoạt động như một đối tác thuần túy đối với <-. Vì vậy, bạn có thể viết lại ví dụ của bạn như:

withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a 
withFile' path mode f = do 
    handle <- openFile path mode 
    let result = f handle 
    hClose handle 
    result 

Trong trường hợp này, bởi vì let là một nhiệm vụ đơn giản, loại resultIO a.

+1

Tuyệt! Tôi gọi '<-' là toán tử suck, vì nó hút giá trị của rhs :-) – drozzy

Các vấn đề liên quan