۲۱ - ۴فانکتورِ توابع
اگه تو REPL بزنین :info Functor
، یکی از نمونههایی که میبینین برای نوعساز ِ نیمه اعمالشده ِ توابعه ((->) r)
:
instance Functor ((->) r)
ممکنه یه ذره گیجکننده باشه، پس ما هم به امید اینکه بیشتر جا بیوفته بازِش میکنیم. اول ببینیم چه کارهایی میشه باهاش کرد:
Prelude> fmap (+1) (*2) 3
7
-- یه ذره جابجایی
Prelude> fmap (+1) (*2) $ 3
7
Prelude> (fmap (+1) (*2)) 3
7
این باید براتون آشنا باشه:
Prelude> (+1) . (*2) $ 3
7
Prelude> (+2) . (*1) $ 2
4
Prelude> fmap (+2) (*1) $ 2
4
Prelude> (+2) `fmap` (*1) $ 2
4
خوشبختانه اینجا چیزی عجیب نیست. اگه تعریفِ نمونه رو در base
ببینین، اینطوریه:
instance Functor ((->) r) where
fmap = (.)
اول تایپها رو باز کنیم. به خاطر داشته باشین که (->)
دو آرگومان میگیره و در نتیجه کایندِش * -> * -> *
است. پس از اول میدونیم که باید یکی از آرگومانها رو اعمال کنیم تا بشه یه Functor
داشته باشیم. در Either Functor
دیدیم که تابع از روی Either a
لیفت میشد و اگه قرار به اعمال شدن بود، به b
اعمال میشد. با تایپِ توابع:
data (->) a b
همون قاعده برقراره: باید از روی ((->) a)
لیفت کرد تا فقط مقدارِ b
تغییر کنه. یه جور رسمه که در این مواقع برای Reader
، بجای a
از r
استفاده شه، ولی هر حرفِ دیگهای هم باشه کار میکنه. اینجا r
اولین آرگومان برای (a -> b)
ِه:
-- نوعساز توابع (type constructor of functions)
(->)
-- تماماً اعمالشده (fully applied)
a -> b
((->) r)
-- معادل است با
r ->
-- تایپ ِ آرگومان تابعه ،r پس
با این متوجه میشیم که r
(تایپِ آرگومان برای توابع) بخشی از ساختاری ِه که یه تابع رو از روش لیفت میکنیم، نه مقداری که تابع روش نگاشت میشه (یا تغییر میکنه).
پس جوابِ تابع همون مقداری میشه که تغییر میکنه. اگه این رو با ترکیبِ توابع مقایسه کنیم، خیلی خوب با هم جور میشن:
(.) :: (b -> c) -> (a -> b) -> a -> c
-- یا شاید
(.) :: (b -> c) -> (a -> b) -> (a -> c)
مقایسهش با Functor
چطور میشه؟
(.) :: (b -> c) -> (a -> b) -> (a -> c)
fmap :: Functor f => (a -> b) -> f a -> f b
از اینجا به بعد دیگه اسمِ توابع و محدودیتهای تایپکلاسی رو حذف میکنیم چون بدیهیاند:
:: (b -> c) -> (a -> b) -> (a -> c)
:: (a -> b) -> f a -> f b
-- تغییر حروف بدون تغییر در مفهوم
:: (b -> c) -> (a -> b) -> (a -> c)
:: (b -> c) -> f b -> f c
-- f ~ ((->) a)
:: (b -> c)
-> (a -> b)
-> (a -> c)
:: (b -> c)
-> ((->) a) b
-> ((->) a) c
-- تغییر گرامر پیشوندی به میانوندی
:: (b -> c) -> (a -> b) -> (a -> c)
:: (b -> c) -> (a -> b) -> (a -> c)
خانمها و آقایان، لیفتِ فانکتوری برای توابع!