۲۹ - ۵کمدقتیِ غیرقابل تحملِ try کردن
یه آزمایشِ کوچولوی دیگه انجام بدیم:
import Control.Exception
canICatch :: Exception e
=> e
-> IO (Either ArithException ())
canICatch e =
try $ throwIO eاینجا throwIO جدیده، تابعیه که میشه باهاش استثنا انداخت. چیزی که اینجا میخوایم نشون بدیم اینه که این عهدهگیرنده نمیتونه همهی تایپهای استثنا رو بگیره، پس ما هم از throwIO استفاده میکنیم تا تایپهای مختلفِ استثنا رو بندازیم.
اینجا Left فقط میتونه یه ArithException رو بگیره یا مدیریت کنه، نه هیچ گونهی دیگهای رو. پس اگه یه تایپِ استثنا ِ دیگهای بندازیم، چنین چیزی میگیریم:
Prelude> canICatch DivideByZero
Left divide by zero
Prelude> canICatch StackOverflow
*** Exception: stack overflow
Prelude> :t DivideByZero
DivideByZero :: ArithException
Prelude> :t StackOverflow
StackOverflow :: AsyncExceptionحالتِ دوم از زیرِ دستِ try در رفت، چون تلاشمون گرفتنِ یه ArithException بود، نه یه AsyncException.
چندین بار اشاره کردیم که SomeException روی همهی تایپهایی که تایپکلاسِ Exception دارن منطبق میشه، پس سعی کنین کُدِ بالا رو طوری بنویسین که StackOverflow یا هر استثنا ِ دیگهای رو بگیره.
با یه برنامهای که انقدر اجرا میشه تا یه استثنا ِ مدیریتنشده جلوش رو بگیره، آزمایشمون رو ادامه میدیم:
module StoppingTheParty where
import Control.Concurrent (threadDelay)
import Control.Exception
import Control.Monad (forever)
import System.Random (randomRIO)
randomException :: IO ()
randomException = do
i <- randomRIO (1, 10 :: Int)
if i `elem` [1..9]
then throwIO DivideByZero
else throwIO StackOverflow
main :: IO ()
main = forever $ do
let tryS :: IO ()
-> IO (Either ArithException ())
tryS = try
_ <- tryS randomException
putStrLn "Live to loop another day!"
-- میکروثانیه
threadDelay (1 * 1000000)
-- ^--------^
-- م. تأخیرِ ریسهی پردازشforever رو قبلاً گفته بودیم؛ میذاره برنامه همینطور بینهایت اجرا بشه. threadDelay رو اضافه کردیم تا حلقه آرومتر بشه و اتفاقاتی که میوفته رو بهتر بشه دید. دقت کنین واحد زمانیش به میکروثانیهست.
tryS باعث میشه ArithExceptionها به عهده گرفته بشن. اون استثناها کنار زده میشن و حلقه ادامه پیدا میکنه. بالاخره وقتی اون عددِ تصادفی ۱۰ بشه، بجای ArithException، یه استثنا ِ AsyncException انداخته میشه و برنامهمون به سرعت میمیره. سعی کنین کاری کنین هردوتا استثنا به عهده گرفته بشن و حلقه ِ برنامه هیچ وقت تموم نشه.