۲۴ - ۳دوتا فانکتور کوچولو رو درخت نشستن لیفت می‌کنن

کارمون رو با ترکیب ِ فانکتورها، به کمک تایپ‌هایی که بالاتر دیدیم شروع می‌کنیم. می‌دونیم که میشه از رو ‏‎Identity‎‏ لیفت کرد؛ این ‏‎Functor‎‏ رو قبلاً دیدین:

instance Functor Identity where
  fmap f (Identity a) = Identity (f a)

فانکتوری که برای ‏‎Identity‎‏ هست در واقع هیچ کاری نمی‌کنه، فقط ماهیتِ ‏‎Functor‎‏ رو نشون میده. تابع از روی بافت ِ ‏‎Identity‎‏ لیفت میشه و بعد روی مقدارِ ‏‎a‎‏ نگاشت میشه.

اگه از ‏‎f‎‏ و ‏‎g‎‏ نمونه ِ ‏‎Functor‎‏ بخوایم، برای ‏‎Compose‎‏ هم میشه یه نمونه ِ ‏‎Functor‎‏ نوشت:

instance (Functor f, Functor g) =>
         Functor (Compose f g) where
  fmap f (Compose fga) =
    Compose $ (fmap . fmap) f fga

حالا ‏‎f‎‏ و ‏‎g‎‏ هردوشون جزئی از ساختاری اند که از روش لیفت می‌کنیم، پس جفت‌شون هم باید ‏‎Functor‎‏ باشن. برای اینکه بتونیم تابعی رو به مقداری که کاملاً داخلِ تایپ قرار گرفته اعمال کنیم، باید بتونیم از روی هردوتای اون لایه‌ها بِپَریم. به خاطرِ وجودِ اون دو لایه، باید دوبار ‏‎fmap‎‏ کنیم.

اگه برگردیم به مثالی که بالاتر داشتیم:

newtype Compose f g a =
  Compose { getCompose :: f (g a) }
  deriving (Eq, Show)

Compose { getCompose :: f (g    a) }
               Compose [] Maybe Int

و از نمونه ِ ‏‎Functor‎‏ استفاده کنیم، می‌تونیم یه تابع رو به اون مقدارِ ‏‎Int‎‏ که لای همه‌ی اون ساختارها پوشونده شده اعمال کنیم:

Prelude> let xs = [Just 1, Nothing]
Prelude> Compose xs
Compose {getCompose = [Just 1,Nothing]}
Prelude> fmap (+1) (Compose xs)
Compose {getCompose = [Just 2,Nothing]}

این قضیه رو میشه به تعدادِ مختلفِ لایه‌ها تعمیم داد، مثلِ زیر که یه لایه کمتر داره. این مثال شاید یادتون بیاد (از یکی از فصل‌های قبل):

newtype One f a =
  One (f a)
  deriving (Eq, Show)

instance Functor f =>
         Functor (One f) where
  fmap f (One fa) = One $ fmap f fa

یا یه لایه ساختار بیشتر از ‏‎Compose‎‏:

newtype Three f g h a =
  Three (f (g (h a)))
  deriving (Eq, Show)

instance (Functor f, Functor g, Functor h) 
      => Functor (Three f g h) where
  fmap f (Three fgha) =
    Compose $ (fmap . fmap . fmap) f fgha

مشابه تایپ ضرب ِ بی‌نام (یعنی ‏‎(,)‎‏) و تایپ جمع ِ بی‌نام (‏‎Either‎‏ تایپِ ‏‎Compose‎‏ هم میشه به تعدادِ دلخواه تودرتو کرد:

v :: Compose []
             Maybe
             (Compose Maybe [] Integer)
v = Compose [Just (Compose $ Just [1])]

میشه اینطوری بهش فکر کرد که ترکیب ِ دو نوع‌داده‌ای که نمونه ِ ‏‎Functor‎‏ دارن، باعث ایجادِ یه نمونه ِ ‏‎Functor‎‏ ِ دیگه میشه. گاهی اوقات چنین چیزی رو اینطوری توصیف می‌کنن که فانکتورها تحتِ عملِ ترکیب بسته‌اند، یعنی وقتی دوتا ‏‎Functor‎‏ رو با هم ترکیب می‌کنین، یه ‏‎Functor‎‏ ِ دیگه می‌گیرین.