۱۷ - ۳فانکتور و اپلیکتیو
قبلاً گفتیم که اپلیکتیوها، فانکتورهای مانویدی اند. پس چیزهایی که از 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)
تایپِ سمت چپ بخشی از ساختار ِه، و اعمال ِ تابع تغییری روی اون ساختار نمیده.