۲۴ - ۹پیداکردن یک الگو

ترانسفورمرها در واقع حامل اطلاعات مشخص از یک تایپ‌اند که میشه باهاشون موندهای بزرگتر درست کرد. تلفیق ساختارها در چنین تودرتویی‌ای:

(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

این یه قاعده‌ی کلی برای تایپِ همه‌ی ترانسفورمرها نیست، فقط قراره یه درکِ کلی از نیاز به ترانسفورمرها بده.