۱۶ - ۱۳ساختار بیشتر، فانکتور بیشتر

گاهی پیش میاد که ساختار ِ یه تایپ، برای داشتنِ یه نمونه ِ ‏‎Functor‎‏، احتیاج داشته باشه که یکی دیگه از لایه‌های میانیِ تایپ‌هاش هم نمونه ِ ‏‎Functor‎‏ داشته باشه. چنین چیزی رو با نوع‌داده ِ زیر نشون میدیم:

data Wrap f a where
  Wrap (f a)
  deriving (Eq, Show)

دقت کنین که ‏‎a‎‏ اینجا یه آرگومان برای ‏‎f‎‏ ِه. خوب چطور یه نمونه ِ ‏‎Functor‎‏ برای این بنویسیم؟

instance Functor (Wrap f) where
  fmap f (Wrap fa) = Wrap (f fa)

این کار نمی‌کنه، چون یه ‏‎f‎‏ اون وسط هست که از روش نمی‌پریم، و ‏‎a‎‏ (مقداری که ‏‎fmap‎‏ باید تابع رو بهش اعمال کنه) یه آرگومان برای اون ‏‎f‎‏ هست – تابع نمی‌تونه به اون ‏‎f‎‏ که ‏‎a‎‏ رو پوشونده اعمال بشه.

instance Functor (Wrap f) where
  fmap f (Wrap fa) = Wrap (fmap f fa)

اینجا تایپِ ‏‎f‎‏ رو نمی‌دونیم، و ممکنه هرچیزی باشه، ولی برای اینکه بشه از روش ‏‎fmap‎‏ کرد، حتماً باید تایپی باشه که یه نمونه ِ ‏‎Functor‎‏ داشته باشه. پس محدودیت رو اضافه می‌کنیم:

instance Functor f
      => Functor (Wrap f) where
  fmap f (Wrap fa) = Wrap (fmap f fa)

حالا اگه این نمونه ِ آخر رو بارگذاری کنیم، چنین نتیجه‌هایی میده:

Prelude> fmap (+1) (Wrap (Just 1))
Wrap (Just 2)

Prelude> fmap (+1) (Wrap [1, 2, 3])
Wrap [2,3,4]

باید برای هر ‏‎Functor‎‏ ای کار کنه. اگه یه چیزی که ‏‎Functor‎‏ نیست بهش بدیم چی؟

Prelude> let n = 1 :: Integer
Prelude> fmap (+1) (Wrap n)

Couldn't match expected type ‘f b’
  with actual type ‘Integer’
Relevant bindings include
it :: Wrap f b (bound at <interactive>:8:1)
In the first argument of ‘Wrap’, namely ‘n’
In the second argument of ‘fmap’,
  namely ‘(Wrap n)’

اون عدد به تنهایی، ساختار ِ اضافی‌ای که ‏‎Wrap‎‏ لازم داره تا به عنوانِ یه ‏‎Functor‎‏ عمل کنه رو ارائه نمیده. انتظار داره بتونه از روی یه ‏‎f‎‏ مستقل از ‏‎a‎‏، fmap کنه، که چنین چیزی با هیچ ثابتِ تایپ ای مثلِ ‏‎Integer‎‏ ممکن نیست.