۲۵ - ۴ترانسفورمرِ Reader یا ReaderT

در برنامه‌هایی که با هسکل نوشته میشن، ‏‎ReaderT‎‏ یکی از پرکاربردترین ترانسفورمر‌هاست. خیلی شبیهِ ‏‎Reader‎‏ ِه، فقط در خروجیِ تابع یه ساختار ِ اضافه ایجاد میشه:

newtype ReaderT r m a = 
  ReaderT { runReaderT :: r -> m a }

مقدارِ داخلِ ‏‎ReaderT‎‏ یه تابع‌ه. اولین آرگومانِ اون تابع، جزئی از ساختار ایه که باید از روش بایند کنیم.

این بار نمونه‌ها رو براتون می‌نویسیم. پس اگه خودتون می‌خواین بنویسین، جلوتر رو نخونین!

instance (Functor m)
      => Functor (ReaderT r m) where
  fmap f (ReaderT rma) =
    ReaderT $ (fmap . fmap) f rma

instance (Applicative m)
      => Applicative (ReaderT r m) where
  pure a = ReaderT (pure (pure a))
  (ReaderT fmab) <*> (ReaderT rma) =
    ReaderT $ (<*>) <$> fmab <*> rma

instance (Monad m)
      => Monad (ReaderT r m) where
  return = pure
  (>>=) :: ReaderT r m a
        -> (a -> ReaderT r m b)
        -> ReaderT r m b
  (ReaderT rma) >>= f =
    ReaderT $ \r -> do
--           [1]
      a <- rma r
--     [3] [ 2 ]
      runReaderT (f a) r
--       [5]     [ 4 ] [6]

۱.

باز هم تایپِ مقدارِ داخلِ ‏‎ReaderT‎‏ باید تابع باشه، پس با گذاشتنِ یه تابع بی‌نام (با یک آرگومان، که برای راحتی، اسمِ متغیرش رو گذاشتیم ‏‎r‎‏) داخلِ یه داده‌ساز ِ ‏‎ReaderT‎‏، تایپ‌ها رو جور کردیم.

۲.

مقدارِ ‏‎r -> m a‎‏ رو با تطبیق الگو از داده‌ساز ِ ‏‎ReaderT‎‏ خارج کردیم و به متغیرِ ‏‎rma‎‏ انقیاد دادیم. و حالا داریم به اون ‏‎r‎‏ که در بدنه ِ لامبدای بی‌نام انتظار داریم اعمال‌ِش می‌کنیم.

۳.

نتیجه‌ی حاصل از اعمال ِ ‏‎r -> m a‎‏ به یه مقدار از تایپِ ‏‎r‎‏ میشه ‏‎m a‎‏. برای اینکه بتونیم تابعِ ‏‎a -> ReaderT r m b‎‏ رو اعمال کنیم، به یه مقدار با تایپِ ‏‎a‎‏ احتیاج داریم. می‌تونیم با استفاده از ‏‎(<-)‎‏، ‏‎a‎‏ رو از داخلِ ساختار ِ ‏‎m‎‏ به بیرون بایند ‏‎(<-)‎‏ کنیم. اون مقدار رو به اسمِ ‏‎a‎‏ بایند کردیم تا تایپ‌ش رو راحت‌تر به خاطر داشته باشیم.

۴.

اعمالِ ‏‎f‎‏ (که تایپِ ‏‎a -> ReaderT r m b‎‏ داره) به مقدارِ ‏‎a‎‏، یه مقدار با تایپِ ‏‎ReaderT r m b‎‏ نتیجه میده.

۵.

از داخل ساختار ِ ‏‎ReaderT‎‏، مقدارِ ‏‎r -> m b‎‏ رو درمیاریم.

۶.

نهایتاً ‏‎r -> m b‎‏ ِ حاصل رو به ‏‎r‎‏ که در شروعِ لاندا داشتیم اعمال می‌کنیم (همون آرگومانی که ‏‎Reader‎‏ انتزاعی می‌کنه). این تابع بی‌نام باید ‏‎m b‎‏ برگردونه، در غیرِ اینصورت تابعِ معتبری نمیشه. برای معتبر بودن باید تایپِ‌ش ‏‎r -> m b‎‏ باشه.

این دفعه تمرین نمیدیم، حق‌تونه یه استراحتی بکنین.