۲۸ - ۸خب حالا چطور MVar کنیم؟
قبلتر توی فصل، یه مثال با استفاده از تایپِ MVar از زمانی که IO جلوی اشتراکگذاری رو میگیره نشون دادیم. کُدِ قبلیمون بلوکه میشد چون این:
myData :: IO (MVar Int)
myData = newEmptyMVarیه اجراییه ِ IO که یه MVar ِ خالی درست میکنه؛ یه ارجاعِ پایدار به یک MVar ِ مشخص نیست. از دو راه میشه درستش کرد. یکی اینکه ارجاعِ پایدار رو به عنوان آرگومان پاس کنیم. کدِ زیر با موفقیت تا انتها اجرا میشه:
module WhatHappens where
import Control.Concurrent
main :: IO ()
main = do
mv <- myData
putMVar mv (0 :: Int)
zero <- takeMVar mv
print zeroاز یه راهِ خبیثانهتر و غیرضروری هم میشه این کار رو کرد. از این فرصت استفاده میکنیم تا یه راهِ ناامنِ فعالکردنِ اشتراکگذاری برای یه اجراییه ِ IO رو بررسی کنیم: unsafePerformIO! کُدِ زیر هم تا انتها اجرا میشه:
module WhatHappens where
import Control.Concurrent
import System.IO.Unsafe
myData :: MVar Int
myData = unsafePerformIO newEmptyMVar
-- ^-------------^
-- IO م. اجرای ناامنِ
main :: IO ()
main = do
putMVar myData 0
zero <- takeMVar myData
print zeroتایپِ unsafePerformIO اینه: IO a -> a، که ظاهراً غیرممکنه و در کل ایدهی خوبی نیست. در کدِ واقعی باید از ارجاعهای MVar به عنوانِ آرگومان، یا به واسطهی ReaderT استفاده کنین، اما ترکیب ِ MVar و unsafePerformIO این فرصت رو به ما میده که خیلی رُک و واضح کاری که unsafePerformIO در کُد انجام میده رو نشون بدیم. اینطوری میشه بجای اینکه هر بار یه MVar ِ جدید ساخت، MVar ِ خالی رو بصورتِ ضمنی و دفعاتِ نامحدود به اشتراک گذاشت.
وقتی لازم نیست، یا جایی که ممکنه شفافیت مرجع در کُدتون رو بِهم بزنه از unsafePerformIO استفاده نکنین! اگه مطمئن نیستین... استفاده نکنین! تابعهای IO ِ غیرِامن ِ دیگهای هم هستن، ولی خیلی کم پیش میاد که لازم بشن، کلاً بهتره صریح رو به ضمنی ترجیح بدین.