۱۷ - ۳فانکتور و اپلیکتیو

قبلاً گفتیم که اپلیکتیو‌ها، فانکتور‌های مانویدی اند. پس چیزهایی که از ‏‎Monoid‎‏ و ‏‎Functor‎‏ یاد گرفتیم به یادگیریِ ‏‎Applicative‎‏ هم ربط دارن. یه کم این رابطه رو توضیح دادیم، ولی می‌خوایم درکِ عمیق‌تری پیدا کنیم.

اول تفاوتِ بین ‏‎fmap‎‏ و ‏‎<*>‎‏ رو دوره کنیم:

fmap  ::   (a -> b) -> f a -> f b
(<*>) :: f (a -> b) -> f a -> f b

اون ‏‎f‎‏ پشتِ تابعِ ‏‎(a -> b)‎‏، تنها فرقِ بین این دوتاست؛ ولی قدرتی که اضافه می‌کنه خیلی زیاده. یه دلیل‌ش اینه که هر ‏‎Applicative‎‏، یه نمونه ِ ‏‎Functor‎‏ هم داره؛ نه فقط بر مبنای تعریف – یه ‏‎Functor‎‏ رو میشه بر مبنای نمونه ِ ‏‎Applicative‎‏ تعریف کرد. از این کتاب خارج ِه، اما از قانون‌های ‏‎Functor‎‏ و ‏‎Applicative‎‏ نتیجه میشه (قوانینِ ‏‎Applicative‎‏ رو تو همین فصل میگیم):

fmap f x = pure f <*> x

اگه بخوایم این رو تو REPL ببینیم، باید اول ‏‎Control.Applicative‎‏ رو وارد کنیم (البته اگه از GHC ۷٫۸ یا قدیمی‌تر استفاده می‌کنین):

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

Prelude> pure (+1) <*> [1..3]
[2,3,4]

با توجه به تایپِ ‏‎pure‎‏ که معادلِ ‏‎Applicative f => a -> f a‎‏ هست، میشه این تابع رو به چشمِ راهی برای قرار دادن ِ یه مقدار (با هر تایپی) به داخلِ ساختاری که باهاش سروکار داریم ببینیم:

Prelude> pure 1 :: [Int]
[1]
Prelude> pure 1 :: Maybe Int
Just 1
Prelude> pure 1 :: Eihter a Int
Right 1
Prelude> pure 1 :: ([a], Int)
([],1)

با تایپِ سمت چپ در دو مثالِ آخر، یه طور دیگه رفتار شد. دلیل‌ش هم مشابهِ مثال زیره:

Prelude> fmap (+1) (4, 5)
(4,6)

تایپِ سمت چپ بخشی از ساختار ِه، و اعمال ِ تابع تغییری روی اون ساختار نمیده.