۲۵ - ۸بیرونیترین ساختار، داخلیترین واژهست
یکی از مواردِ سختِ موند ترانسفورمرها اینه که ارائهی واژگانی ِ تایپها در رابطه با ساختار ِ مقادیر، برخلاف چیزیه که در نگاه اول به نظر میاد. اجازه بدین به نکتهای در تعاریفِ این تایپها اشاره کنیم:
-- transformers این تعاریف در کتابخونهی
-- ممکنه کمی فرق داشته باشن. مهم نیست.
newtype ExceptT e m a =
ExceptT { runExceptT :: m (Either e a) }
newtype MaybeT m a =
MaybeT { runMaybeT :: m (Maybe a) }
newtype ReaderT r m a =
ReaderT { runReaderT :: r -> m a }یکی از مشخصههای واجب در ترانسفورمرها اینه که ساختار ِ اضافیِ m همیشه دور مقدار رو پوشونده. به این هم دقت کنین که اون ساختار همیشه چیزی رو پوشونده که در اختیار داریم، نه چیزی که لازم داریم، مثلِ ReaderT. پیآمدِ چنین چیزی اینه که یک سری از موند ترانسفورمرها در یه تایپ، از داخلیترین تایپ (از لحاظِ ساختاری) شروع میشه. مثال زیر رو در نظر بگیرین:
module OuterInner where
import Control.Monad.Trans.Except
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Reader
-- استفاده return فقط کافیه یکبار از
-- گنده داریم. Monad کنیم، چون فقط یه
embedded :: MaybeT
(ExceptT String
(ReaderT () IO))
Int
embedded = return 1میشه لایهلایه "پوست" بِکَنیم:
maybeUnwrap :: ExceptT String
(ReaderT () IO) (Maybe Int)
maybeUnwrap = runMaybeT embedded
-- بعدی
eitherUnwrap :: ReaderT () IO
(Either String (Maybe Int))
eitherUnwrap = runExceptT maybeUnwrap
-- و در آخر
readerUnwrap :: ()
-> IO (Either String
(Maybe Int))
readerUnwrap = runReaderT eitherUnwrapبعد اگه بخوایم این کُد رو محاسبه کنیم، مقدارِ واحد رو به تابع میدیم:
Prelude> readerUnwrap ()
Right (Just 1)چرا این جواب؟ دقت کنین که از return برای یه Monad متشکل از Reader، Either، و Maybe استفاده کردیم:
instance Monad ((->) r) where
return = const
instance Monad (Either e) where
return = Right
instance Monad Maybe where
return = Justمیشه استفاده از return برای دسته ِ Reader/Either/Maybe رو به چشمِ ترکیب نگاه کنیم. ببینید چطور کُدِ زیر همون جوابِ readerUnwrap () رو میده:
Prelude> (const . Right . Just $ 1) ()
Right (Just 1)یه نکتهی که در خوندنِ موند ترانسفورمرها باید به خاطر داشته باشین اینه که وقتی هسکلنویسها میگن موندِ پایه، معمولاً منظورشون بیرونیترین از لحاظِ ساختاری ِه.
type MyType a = IO [Maybe a]در MyType، موندِ پایه میشه IO.
تمرین: بپیچونین
تابعِ readerUnwrap از مثالِ قبل رو با استفاده از دادهسازهای هر ترانسفورمر به embedded برگردونین.
-- تغییرش بدین تا کار کنه.
embedded :: MaybeT
(ExceptT String
(ReaderT () IO))
Int
embedded = ??? (const (Right (Just 1)))