2013-08-02 8 views
16

私はこの長期的なサンクを作成し、最終的には例外で失敗する小さなプログラムを作成しました。次に、複数のスレッドが評価しようとします。サンクが発生した場合、そのサンクの結果として例外が保持されますか?

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 

スレッドの数を増やす明らか失敗したサンクは、結果として例外を維持することを示唆している計算、に影響を与えません。本当ですか?この動作は文書化されているかどこかに指定されていますか?

更新:

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

forkFinallyラインを変更するには、各スレッドが例外で失敗したことを確認します。

+1

例外*は*式の値です。他に何回表現を評価することができますか? – Carl

+0

@Carlそれは私が疑うところですが、私は確信したいと思います。また、値を何度も再計算してみることもできます。 –

+0

私はGHCの内部構造を知っています。そうでなければ 'ghc-heap-view'のようなツールを作ることができませんでしたので、もっと何が必要なのか分かりません。私の答えが十分役に立たない場合は、あなたの質問を明確にしてもらえますか? –

答えて

12

ghc-heap-viewライブラリを使用してGHCが実際にどのようにこれを行うかを示すことでこの質問に答えてください。あなたはおそらくghc-visとこれを再現し、素敵な写真を得ることができます。

私はどこかの例外値とデータ構造を作成することから始め:

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

最初は、それは純粋なサンクである(つまり、様々な種類のクラスが関与しているようだ):

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)() 

今、私は

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

リストの2番目の要素はstilです。私はちょうど "正常な"サンク。今、私は例外を取得、これを評価し、再びそれを見て:

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())] 

あなたはそれが今SomeExceptionオブジェクトを参照サンクで見ることができます。 SomeExceptionデータコンストラクタのタイプはforall e . Exception e => e -> SomeExceptionなので、コンストラクタの2番目のパラメータはArithException例外のDivideByZeroコンストラクタであり、最初のパラメータは対応するExceptionタイプのクラスインスタンスです。

このサンクは、他のハスケルの値と同じように渡すことができ、評価された場合には再び例外が発生します。そして、ちょうど他の値と同様に、例外を共有することができます。

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) 

同じことは、スレッドとMVarsとそこに特別な何も起こりません。

関連する問題