۲۴ - ۹پیداکردن یک الگو
ترانسفورمرها در واقع حامل اطلاعات مشخص از یک تایپاند که میشه باهاشون موندهای بزرگتر درست کرد. تلفیق ساختارها در چنین تودرتوییای:
(Monad ma) => m (m a)با join انجام میشه. ترانسفورمرها زمانی کاربرد دارن که میخوایم از عملیات ِ >>= روی f و g، که تایپهای متفاوتی دارن (اما هردو نمونههای Monad دارن) استفاده کنیم. پس یه تایپِ جدید درست میکنیم و اسمش رو میذاریم موند ترانسفورمر، و با نوشتن نمونه ِ Monad برای اون تایپ، ساختار ِ اضافیش رو مدیریت میکنیم.
الگو ِ کلی از این قراره: میخوایم دوتا تایپِ پلیمورفیک، f و g، که هر کدوم نمونه ِ Monad هم دارن رو با هم ترکیب کنیم. اما نهایتاً به چنین الگویی میرسیم:
f (g (f b))با وجودِ اون g وسط اون دوتا، بایند ِ Monad نمیتونه اون تایپها رو با هم تلفیق کنه. پس اول باید برسیم به این:
f (f b)تنها راهش اینه که از طریقی بشه اون g رو فولد کرد. با Monad نمیشه. ماهیتِ Monad، تابعِ join ِه، ولی اینجا فقط یک ساختار ِ g داریم، نه دوتا g (g...)، پس موند کافی نیست. راهِ سرراست اینه که g رو معین کنیم. با اطلاعات مشخص از اون ساختار ِ میانی، میشه فولدِش کرد. خبر خوب اینکه در ترانسفورمرها لازم نیست f مشخص باشه؛ فقط باید یه نمونه Monad داشته باشه، در نتیجه کافیه برای هر تایپ فقط یک بار ترانسفورمر بنویسیم.
این الگو رو با IdentityT هم میشه دید. شاید این مرحله از نوشتنِ نمونه ِ Monadِش خاطرتون باشه:
(IdentityT ma) >>= f =
let aimb :: m (IdentityT m b)
aimb = fmap f maاین تایپچک میشه، اما هنوز اون شکلی که میخوایم رو نداره. اول باید اون IdentityT رو فولد کنیم تا بتونیم از join که به واسطهی Monad m => m داریم، استفاده کنیم. پس میرسیم به مرحلهی بعد:
let aimb :: m (m b)
aimb = fmap runIdentityT (fmap f ma)حالا که دستگیرهی رکورد ِ IdentityT رو از روی m لیفت کردیم، رسیدیم به چیزی که میتونیم join کنیم! به خاطرِ سادگیِ IdentityT، همون دستگیرهی رکورد برای فولدکردن ِ ساختارِش کفایت میکنه. از این نقطه، پیادهسازیِ این فرایند کارِ سادهایه:
m (m b) -> m b -> IdentityT m bاون تایپ آخر، همون تایپیه که در تعریفِ (>>=) برای IdentityT باید بهش برسیم.
فرایندِ زیر، اساسِ الگو ایه که موند ترانسفورمرها باهاش کار میکنن. m ساختار ِ پلیمورفیک و بیرونیه، و T یه تایپِ معین ِه که ترانسفورمر برای اون تعریف شده. مثلاً در ترانسفورمر ِ بالا، T میشه IdentityT.
m (T m b)
-> m (m b)
-> m b
-> T m bاین یه قاعدهی کلی برای تایپِ همهی ترانسفورمرها نیست، فقط قراره یه درکِ کلی از نیاز به ترانسفورمرها بده.