2013-08-02 23 views
16

Uzun süreli çalışan bir thunk oluşturan ve bu durumun bir istisna ile sonuçlanmayan küçük programını oluşturdum. Daha sonra, birden çok konu onu değerlendirmeye çalışır. parçacığı sayısını arttırmakBir thunk bir istisna ile sonuçlanırsa, thunk sonucu tutulan istisna mıdır?

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 

açıkça başarısız bir thunk sonucu istisna tutar düşündürmektedir hesaplama, üzerinde herhangi bir etkisi yoktur. Bu doğru mu? Bu davranış bir yerde belgelenmiş/belirtilmiş midir?

Güncelleme:

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

için forkFinally hattını değiştirme Her iş parçacığı durumla başarısız olduğunu teyit etmektedir.

+1

Kural dışı durum * ifadenin değeridir. İfadeyi birden çok kez değerlendiren başka ne yapabilir? – Carl

+0

@Carl Şüpheliyim, ama emin olmak istiyorum. Aynı zamanda değeri tekrar tekrar hesaplamayı da deneyebilir. –

+0

GHC internals biliyorum, aksi takdirde ghc-heap-view' gibi araçlar oluşturamadım, bu yüzden daha fazla ihtiyacınız olandan emin değilim. Cevabım yeterli değilse, sorunuzu açıklayabilir misiniz? –

cevap

12

Bu sorunun cevabını, GHC'nin bunu nasıl yaptığını göstererek ghc-heap-view kitaplığını kullanarak yanıtlayayım. Bunu muhtemelen ghc-vis ile yeniden üretebilir ve güzel resimler çekebilirsiniz. (Çeşitli tip sınıfları dahil görünüyor) öncelikle thunk saf olduğunu az

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

:

yere bir istisna değerine sahip bir veri yapısını oluşturarak başlamak

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

Şimdi istisnai olmayan atma-parçalarını değerlendirmek:

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

Listenin ikinci öğesi Ben sadece “normal” thunk. Şimdi bu değerlendirmek, istisna almak ve tekrar bakmak:

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

Sen şimdi bir SomeException nesneyle ilgili bir parça var görebilirsiniz. SomeException veri yapıcısı forall e . Exception e => e -> SomeException tipindedir, bu nedenle yapıcının ikinci parametresiistisnasının DivideByZero yapıcısıdır ve ilk parametre karşılık gelen Exception tip sınıf örneğidir.

Bu thunk, diğer herhangi bir Haskell değeri gibi etrafından geçirilebilir ve değerlendirilirse, istisnayı tekrar yükseltir. Ve, tıpkı diğer değer gibi, istisna paylaşılabilir:

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) 

aynı şeyleri parçacığı ve MVars, orada özel bir şey ile olur.