2013-08-02 35 views
16

Tôi tạo ra chương trình nhỏ này tạo ra một đoạn dài chạy mà cuối cùng thất bại với một ngoại lệ. Sau đó, nhiều chủ đề cố gắng để đánh giá nó.Nếu một kết quả trong một ngoại lệ, ngoại lệ có được lưu giữ như là kết quả của thunk không?

import Control.Monad 
import Control.Concurrent 
import Control.Concurrent.MVar 

main = do 
    let thunk = let p = product [1..10^4] 
       in if p `mod` 2 == 0 then error "exception" 
             else() 
    children <- replicateM 2000 (myForkIO (print thunk)) 
    mapM_ takeMVar children 

-- | Spawn a thread and return a MVar which can be used to wait for it. 
myForkIO :: IO() -> IO (MVar()) 
myForkIO io = do 
    mvar <- newEmptyMVar 
    forkFinally io (\_ -> putMVar mvar()) 
    return mvar 

Tăng số lượng chủ đề rõ ràng không ảnh hưởng đến tính toán, điều này cho thấy một đoạn không thành công sẽ giữ ngoại lệ. Có đúng không? Hành vi này có được ghi lại/chỉ định ở đâu đó không?

Cập nhật: Thay đổi dòng forkFinally để

forkFinally io (\e -> print e >> putMVar mvar()) 

khẳng định rằng mỗi thread không thành công với các ngoại lệ.

+1

Ngoại lệ * là * giá trị của biểu thức. Điều gì khác có thể đánh giá biểu thức nhiều lần làm gì? – Carl

+0

@Carl Đó là điều tôi nghi ngờ, nhưng tôi muốn chắc chắn. Nó cũng có thể thử tính lại giá trị một lần nữa và một lần nữa. –

+0

Tôi biết nội bộ GHC, nếu không tôi không thể tạo các công cụ như 'ghc-heap-view', vì vậy tôi không chắc bạn cần thêm gì nữa. Bạn có thể làm rõ câu hỏi của bạn nếu câu trả lời của tôi không đủ hữu ích? –

Trả lời

12

Hãy để tôi trả lời câu hỏi này bằng cách hiển thị cách GHC thực sự thực hiện việc này, sử dụng thư viện ghc-heap-view. Bạn có thể tái tạo hình ảnh này với ghc-vis và nhận những bức ảnh đẹp.

tôi bắt đầu bằng cách tạo ra một cấu trúc dữ liệu với giá trị ngoại lệ đâu đó:

Prelude> :script /home/jojo/.cabal/share/ghc-heap-view-0.5.1/ghci 
Prelude> let x = map ((1::Int) `div`) [1,0] 

Lúc đầu, nó hoàn toàn là một thunk (mà dường như liên quan đến các lớp học kiểu khác nhau):

Prelude> :printHeap x 
let f1 = _fun 
in (_bco [] (_bco (D:Integral (D:Real (D:Num _fun _fun _fun _fun _fun _fun _fun) (D:Ord (D:Eq _fun _fun) _fun _fun _fun _fun _fun _fun _fun) _fun) (D:Enum _fun _fun f1 f1 _fun _fun _fun _fun) _fun _fun _fun _fun _fun _fun _fun) _fun) _fun)() 

Bây giờ tôi đánh giá các bộ phận phi ngoại lệ-ném:

Prelude> (head x, length x) 
(1,2) 
Prelude> System.Mem.performGC 
Prelude> :printHeap x 
[I# 1,_thunk (_fun (I# 1)) (I# 0)] 

Phần tử thứ hai của danh sách là stil Tôi chỉ là một mẩu "bình thường". Bây giờ tôi đánh giá này, có một ngoại lệ, và nhìn vào nó một lần nữa:

Prelude> last x 
*** Exception: divide by zero 
Prelude> System.Mem.performGC 
Prelude> :printHeap x 
[I# 1,_thunk (SomeException (D:Exception _fun (D:Show _fun _fun _fun) _fun _fun) DivideByZero())] 

Bạn có thể nhìn thấy nó bây giờ là một thunk tham chiếu đến một đối tượng SomeException. Trình tạo dữ liệu SomeException có loại forall e . Exception e => e -> SomeException, do đó tham số thứ hai của hàm tạo là hàm tạo DivideByZero của ngoại lệ ArithException và tham số thứ nhất tương ứng với cá thể lớp loại Exception tương ứng.

Thunk này có thể được truyền đi xung quanh giống như bất kỳ giá trị Haskell nào khác và nếu được đánh giá, hãy tăng ngoại lệ một lần nữa. Và, giống như bất kỳ giá trị nào khác, ngoại lệ có thể được chia sẻ:

Prelude> let y = (last x, last x) 
Prelude> y 
(*** Exception: divide by zero 
Prelude> snd y 
*** Exception: divide by zero 
Prelude> System.Mem.performGC 
Prelude> :printHeap y 
let x1 = SomeException (D:Exception _fun (D:Show _fun _fun _fun) _fun _fun) DivideByZero() 
in (_thunk x1,_thunk x1) 

Điều tương tự cũng xảy ra với chủ đề và MV, không có gì đặc biệt ở đó.