۲۴ - ۷مونَد ترانسفورمِر

دیگه مشکلی که با ‏‎Monad‎‏ هست رو دیدیم: میشه دوتا رو با هم ترکیب کنیم، اما ازشون یه نمونه ِ ‏‎Monad‎‏ ِ جدید نمی‌گیریم. هر وقت می‌خوایم یه نمونه ِ ‏‎Monad‎‏ ِ جدید بگیریم، یه موند ترانسفورمر لازم داریم. جادو نیست؛ جواب در تایپ‌هاست.

بالاتر گفتیم که موند ترانسفورمر یه نوع‌ساز ِه که به عنوانِ آرگومان ‏‎Monad‎‏ می‌گیره و در جواب، ‏‎Monad‎‏ برمی‌گردونه. همچنین اشاره کردیم که اتحادِ دو موند ِ ناشناس غیرممکنه، و همین مورد، مشکلِ اساسی در ترکیبِ دوتا مونَد ِه. برای اینکه اون ‏‎join‎‏ کار کنه، باید پلی‌مورفیسم ِ یکی از موندها رو کاهش بدیم و اطلاعاتِ معین ازش بگیریم. اون یکی موند به عنوانِ یه آرگومانِ تایپیِ متغیر برای نوع‌ساز‌ِمون، پلی‌مورفیک باقی می‌مونه. میشه ترانسفورمرها رو دورِ چندتا (۲، ۳، ۴...) تایپ که هرکدوم یه نمونه ِ ‏‎Monad‎‏ دارن پوشوند و ازشون یه موند بدست آورد و از قابلیتِ هر کدوم بهره برد.

تایپ‌ها ممکنه کمی گیج‌کننده باشن، به همین خاطر ما هم نوشتنِ موند ترانسفورمرها رو خیلی آروم پیش میریم. بعضی جاها ممکنه به نظر سخت بیاد، پس با هر سرعتی که صلاح می‌دونین پیش برین.

دسته‌بندی موندی

با ‏‎Applicative‎‏ میشه توابعی که بیشتر از یک آرگومان می‌گیرن رو به مقادیری که زیرِ ساختار‌های فانکتوری هستن اعمال کرد:

-- :از این
fmap (+1) (Just 1)

-- :به این
    (,,)
<$> Just 1
<*> Just "lol"
<*> Just [1, 2]

گاهی اوقات یه بایندی لازم هست که همزمان با بیشتر از یک ‏‎Monad‎‏ کار کنه. چنین چیزی معمولاً در برنامه‌هایی دیده میشه که باید چندتا کار رو با هم انجام بدن، مثل برنامه‌های تحت وب که ترکیب ِ ‏‎Reader‎‏ و ‏‎IO‎‏ داخل‌شون رایج‌ه. ‏‎IO‎‏ برای اجراییه‌های اثردار، مثل تماس با پایگاه داده کاربرد داره، ‏‎Reader‎‏ برای ارتباط(های) پایگاه داده، و یا درخواست‌های HTTP به کار میره. بعضی وقت‌ها ممکنه حتی چندتا ‏‎Reader‎‏ لازم داشته باشین (داده‌ی مختصِ برنامه، در مقابلِ چیزی که چارچوب به صورتِ پیش‌فرض تأمین می‌کنه)، البته معمولاً میشه داده‌ای که می‌خواین رو به یه تایپِ ضرب در یک ‏‎Reader‎‏ بدین.

پس در واقع سؤالی که داریم، اینه که چطور از روی تایپی مثل زیر بایند کنیم؟

IO (Reader String [a])

-- :نمونه‌های موندی که اینجا داریم
-- [] ‎‏، و‏‎Reader ،IO

پیاده‌سازیِ بد

می‌تونیم تایپ‌های یکبار مصرف برای هر ترکیب درست کنیم، اما این کار زود خسته‌کننده میشه. برای مثال:

newtype Maybe IO a =
  MaybeIO { runMaybeIO :: IO (Maybe a) }

newtype MyabeList a =
  MaybeList { runMaybeList :: [Maybe a] }

لازم نیست به چنین کاری متوسل بشیم؛ فقط کافیه یکی از تایپ‌ها برامون معلوم باشه تا بتونیم برای ترکیب‌ش با یه تایپِ دیگه ‏‎Monad‎‏ داشته باشیم. راهِ دوری از ‏‎Monad‎‏‌های یکبارمصرف برای هر ترکیب ِ ممکن از تایپ‌ها، استفاده از موند ترانسفورمر‌هاست.