۱۶ - ۱۸تعاریف
۱.
پلیمورفیسمِ گونهبالا یا 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
رو نتیجه بده. به خاطر داشتنِ این تعریف کمک میکنه؛ یادتون باشه که تایپها رو دنبال کنین.