۱۶ - ۱۸تعاریف

۱.

پلی‌مورفیسمِ گونه‌بالا یا higher-kinded polymorphism نوعی چندریختی ِه که از طریقِ یه متغیر تایپ، تایپ‌های گونه‌بالا رو انتزاعی می‌کنه. ‏‎Functor‎‏ یکی از مثال‌های پلی‌مورفیسم ِ گونه‌بالا به حساب میاد، به خاطرِ اینکه پارامترِ ‏‎f‎‏ برای ‏‎Functor‎‏ از گونه ِ ‏‎* -> * -> *‎‏ هست. یه مثالِ دیگه از پلی‌مورفیسم گونه‌بالا، نوع‌داده‌ای میشه که یکی از پارامترهای نوع‌ساز‌ِش گونه‌بالا باشه، مثل نوع‌داده ِ زیر:

data Weird f a = Weird (f a)

گونه ِ تایپ‌هاش از این قرارند:

a     :: *
f     :: * -> *
Weird :: (* -> *) -> * -> *

اینجا هم ‏‎Weird‎‏ و هم ‏‎f‎‏ گونه‌بالا اند، که ‏‎Weird‎‏ هم یه مثال از پلی‌مورفیسم گونه‌بالا هست.

۲.

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

f      :: a -> b

-- ``ساختارِ بیشتر''
fmap f :: f a -> f b

-- فقط به یک آرگومان اعمال f
-- * -> * شده، پس گونه‌ش میشه

باید دقت کنیم چنین ایده‌ای رو فقط مختصِ ساختارهای داده و جعبه‌ها ندونیم. توابع هم ‏‎Functor‎‏ دارن؛ کلاً خیلی از تایپ‌های عجیب‌وغریب ممکنه نمونه‌های قانونمندِ ‏‎Functor‎‏ داشته باشن.

۳.

نوبت میرسه به لیفت‌کردن یا lifting. مابقی کتاب بیشتر با اَپلیکِتیو‌ها و موند‌ها سروکار داریم و یه عالَم لیفت می‌کنیم، این کار یعنی چی؟ اولین بار که آقای کارنَپ فانکتور‌ها رو در زبان‌شناسی توصیف کرد، اصلاً هیچ اشاره‌ای به لیفت‌کردن ِ چیزی نداشت. ریاضیدان‌ها هم راه‌ش رو پیش گرفتن و فقط به نگاشت‌کردن و تولیدِ خروجی از انواعِ بخصوص ورودی تمرکز کردن. با این حال هسکل‌نویس‌ها از لغت لیفت زیاد استفاده می‌کنن (مثل ما تو این کتاب).

مردم معمولاً از دو جنبه بهش فکر می‌کنن. یکی اینکه میشه یه تابع رو به داخل یه بافت لیفت کنیم. یکی دیگه اینکه میشه یه تابع رو از روی (یا بالاسرِ) یه لایه از ساختار لیفت کنیم تا اعمال‌ِش کنیم. نتیجه یک چیزه:

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

در مثال اول، اون تابع رو به داخل یه بافت ِ ‏‎Maybe‎‏ لیفت کردیم تا اعمال‌ِش کنیم؛ در مثال دوم، داخل یه بافت ِ لیست. این که به چشمِ "لیفت‌کردن ِ تابع به داخلِ بافت" بهش فکر کنیم، به این دلیل مفهوم رو می‌رسونه چون اون بافت ِه که تعیین می‌کنه تابع چطور اعمال بشه (مثلاً به یک مقدار، یا بصورتِ بازگشتی به چند مقدار). منظور از بافت، نوع‌داده، تعریفِ نوع‌داده، و نمونه ِ ‏‎Functor‎‏ که برای اون نوع‌داده داریم هست. این که نتیجه‌ی اعمال ِ تابع به یه ‏‎a‎‏ ای که وجود نداره چی بشه، توسطِ بافت‌ها مشخص میشه:

Prelude> fmap (+1) []
[]
Prelude> fmap (+1) Nothing
Nothing

ولی ما معمولاً لیفت‌کردن رو بلند‌کردنِ یه تابع از روی یه ساختار داده میگیم، مثل وقت‌هایی که میگیم ‏‎fmap‎‏ یه تابع رو از روی یه ساختارِ داده لیفت می‌کنه. اگه اون داده‌ساز رو به چشمِ یه لایه ساختار ببینین، این هم مفهوم رو می‌رسونه. تابع از روی اون لایه می‌پره و به چیزی که اون تو هست اعمال میشه.

مفهوم دقیق‌تر برای لیفت‌کردن، اعمال ِ یه نوع‌ساز به یه تایپ‌ه؛ مثلِ اعمال ِ یه نوع‌ساز ِ ‏‎f‎‏ به یه متغیر تایپ ِ ‏‎a‎‏ تا ‏‎f a‎‏ رو نتیجه بده. به خاطر داشتنِ این تعریف کمک می‌کنه؛ یادتون باشه که تایپ‌ها رو دنبال کنین.