۱۶ - ۳اِفمَپ اینجا، افمپ اونجا، افمپ همهجا
fmap رو قبلاً دیدیم، اما برای چیزی غیر از لیستها ازش استفاده نکردیم. با لیستها انگار عین map کار میکنه:
Prelude> map (\x -> x > 3) [1..6]
[False,False,False,True,True,True]
Prelude> fmap (\x -> x > 3) [1..6]
[False,False,False,True,True,True]البته لیست یکی از تایپهاییه که یه نمونه از تایپکلاسِ Functor داره، ولی استفاده از fmap با لیست، چون دقیقاً مثلِ map کار میکنه، خیلی چیزِ بارِزی نیست. با این حال لیست تنها تایپی که Functor داره نیست، و fmap میتونه یه تابع رو از روی ( یا از اطراف) هر کدوم از اون ساختارهای فانکتوری اعمال کنه، در صورتی که map نمیتونه:
Prelude> map (+1) (Just 1)
Couldn't match expected type ‘[b]’
with actual type ‘Maybe a0’
Relevant bindings include
it :: [b] (bound at 16:1)
In the second argument of ‘map’
namely ‘(Just 1)’
In the expression: map (+1) (Just 1)
Prelude> fmap (+1) (Just 1)
Just 2جالب شد! دیگه چی؟
-- !با یه توپل
Prelude> fmap (10/) (4, 5)
(4,2.0)
-- !Either با
Prelude> let rca = Right "Chris Allen"
Prelude> fmap (++ ", Esq.") rca
Right "Chris Allen, Esq."نحوهی اختصاصی شدنِ تایپِ fmap برای تایپهای مختلف رو میشه اینجا دید:
type E e = Either e
type C e = Constant e
type I = Identity
-- Functor f =>
fmap :: (a -> b) -> f a -> f b
:: (a -> b) -> [ ] a -> [ ] b
:: (a -> b) -> Maybe a -> Maybe b
:: (a -> b) -> E e a -> E e b
:: (a -> b) -> (e,) a -> (e,) b
:: (a -> b) -> I a -> I b
:: (a -> b) -> C e a -> C e bاگه از ۸ GHC یا جدیدتر استفاده میکنین، چنین چیزی رو خودتون هم تو REPL میتونین ببینین:
Prelude> :set -XTypeApplications
Prelude> :type fmap @Maybe
fmap @Maybe ::
(a -> b) -> Maybe a -> Maybe b
Prelude> :type fmap @Either
fmap @Either ::
(a -> b) -> Either t a -> Either t bشاید متوجه شده باشین که در توپل و Either، اولین آرگومانشون ( که در جدولِ بالا با e نشون دادیم) توسطِ fmap نادیده گرفته شده. یه ذره جلوتر دلیلِ چنین چیزی رو میگیم. قبلش ببینیم یه فانکتور چطور درست میشه. بعد برمیگردیم و حسابی این مبحث رو ادامه میدیم.