۱۶ - ۳اِفمَپ اینجا، افمپ اونجا، افمپ همهجا
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
نادیده گرفته شده. یه ذره جلوتر دلیلِ چنین چیزی رو میگیم. قبلش ببینیم یه فانکتور چطور درست میشه. بعد برمیگردیم و حسابی این مبحث رو ادامه میدیم.