۲۹ - ۵کم‌دقتیِ غیرقابل تحملِ 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‎‏ انداخته میشه و برنامه‌مون به سرعت می‌میره. سعی کنین کاری کنین هردوتا استثنا به عهده گرفته بشن و حلقه ِ برنامه هیچ وقت تموم نشه.