۲۴ - ۷مونَد ترانسفورمِر
دیگه مشکلی که با 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
های یکبارمصرف برای هر ترکیب ِ ممکن از تایپها، استفاده از موند ترانسفورمرهاست.