۵ - ۹تعاریف

۱.

پلی‌مورفیسم یا polymorphism اشاره به متغیرهای تایپی‌ای داره که ممکنه نماینده‌ی بیشتر از یک تایپِ معین باشن، و بیشترِ مواقع به صورت پارامتریک و یا اَد-هاک در هسکل دیده میشن. با داشتنِ مجموعه‌ی بزرگتری از تایپ‌ها، میشه به واسطه‌ی نقاطِ مشترکِ‌شون، مجموعه‌ای کوچکتر از اون تایپ‌ها درست کرد. استفاده از این مجموعه‌های کوچکتر، احتمالِ ایراد در برنامه‌هامون رو کم می‌کنه، و قابلیت استفاده‌ی دوباره از کُدمون رو هم بیشتر می‌کنه.

۲.

استنتاج تایپ یا type inference یکی از قابلیت‌های بعضی زبان‌های برنامه‌نویسی، به خصوص هسکل و ML ِه، که به اونها امکانِ نتیجه‌گیری ِ تایپِ اصلی ِ یه جمله، بدون نوشتنِ صریح ِ تایپ‌شون رو میده. گاهی در هسکل پیش میاد که جمله‌ای تایپ‌های مناسبی داره، اما تایپِ اصلی نداره؛ که اینجور مواقع باید تایپ صراحتاً اعلام بشه.

منظور از تایپِ اصلی در هسکل، جامع‌ترین تایپ ممکنه که تایپچک میشه. مفهومِ کلی‌ترِ اون رو بخوایم بگیم، تایپِ اصلی یکی از خصوصیاتِ تایپ سیستم‌ای‌ِه که باهاش تعامل می‌کنین. یه تایپ سیستم وقتی اصالتِ تایپ داره که همه‌ی تایپ‌های ممکنِ یه جمله در یک محیط، نمونه‌ای از یک تایپِ اصلی باشن. چندتا مثال ببینیم:

-- در تایپ‌های
a
Num a => a
Int

-- با پلی‌مورفیسم پارامتریک a تایپ
-- اینجا تایپ اصلی میشه

-- در تایپ‌های
(Ord a, Num a) => a
Integer

-- تایپ اصلی میشه
-- (Ord a, Num a) => a

۳.

متغیر تایپ یا type variable برای اشاره به یک تایپ یا یه مجموعه‌ای از تایپ‌های نامشخص در تایپ سیگنچرهای هسکل به کار میره. در یک تایپ سیگنچر، متغیرهای تایپ با اسم یکسان با هم برابراند. چند نمونه:

id :: a -> a

-- ،که دو بار ظاهر شده a یک متغیر تایپ
-- یه بار به عنوان ورودی و یه بار به
-- عنوان خروجی. به خاطر پلی‌مورفیسم
-- .پارامتریک می‌تونه هر چیزی باشه


(+) :: Num a => a -> a -> a

-- ملزوم به داشتن ،a یک متغیر تایپ
-- ،دو بار آرگومان .Num یک نمونه از
-- .یه بار جواب. همه هم یک تایپ اند

۴.

تایپکلاس یا typeclass راهی برای بیانِ امکاناتی‌ه که بین چند تایپ مشترک‌ِه. این اشتراک‌ها به ما کمک می‌کنن کُدمون رو برای تک‌تکِ تایپ‌ها تکرار نکنیم، مثل جمع کردن مقادیرِ تایپ‌های ‏‎Int‎‏، ‏‎Integer‎‏، ‏‎Float‎‏، ‏‎Double‎‏، و ‏‎Rational‎‏ با هم. بقیه‌ی توابع مثل ‏‎(*)‎‏، ‏‎(-)‎‏، ‏‎negate‎‏ و غیره هم لازم نیست برای تک‌تکِ تایپ‌ها تعریف بشن، فقط کافیه همه‌ی اونها رو تو یه تایپکلاس تعریف کنیم و نمونه ای از اون تایپکلاس رو به تایپ‌های مختلف بدیم. برای مثال، توابع بالا در تایپکلاس ‏‎Num‎‏ تعریف شدن، پس میشه از اون توابع برای هر تایپی که یه نمونه از ‏‎Num‎‏ داشته باشه استفاده کرد. بنابراین، به کمک تایپکلاس‌ها می‌تونیم کُدمون رو برمبنای امکانات تعریف شده در یه تایپکلاس بنویسیم و توابعِ برنامه‌مون با همه‌ی تایپ‌هایی که یه نمونه از اون تایپکلاس دارن سازگار میشن، چه اون تایپ‌ها وجود داشته باشن، چه هنوز اختراع نشده باشن (شاید به دست شما بشن).

۵.

پارامتریسیته یا parametricity خاصیتی‌ِه که در صورتِ وجودِ پلی‌مورفیسم ِ پارامتریک حفظ میشه. پارامتریسیته یعنی رفتار یه تابع برای تایپ‌های معین ِ مختلف یکسان می‌مونه. پارامتریسیته* میگه که تابعِ:

id :: a -> a

به ازای همه‌ی تایپ‌های هسکل رفتار دقیقاً یکسانی داره، بدون اینکه لازم باشه چیزی از طرز کارش بدونیم. به خاطر این خاصیت، می‌دونیم که تابعِ:

const :: a -> b -> a

حتماً آرگومان اول‌ش رو برمی‌گردونه – پارامتریسیته و تعریفِ تایپ چنین رفتاری رو تضمین می‌کنن.

f :: a -> a -> a

اینجا، تابع ‏‎f‎‏ فقط و فقط می‌تونه مقدار اول یا دوم رو برگردونه، و همیشه بدونِ تغییر همین کار رو انجام میده. اگه از ‏‎(+)‎‏ یا ‏‎(*)‎‏ در تابع ‏‎f‎‏ استفاده میشد، اون موقع تایپ‌ش ملزم به داشتن تایپکلاس ‏‎Num‎‏ می‌بود، و متعاقباً بجای پارامتریک، چندریختیِ اد-هاک میداشت.

blahFunc :: b -> String

تابع ‏‎blahFunc‎‏ آرگومان‌ش رو کلاً نادیده می‌گیره؛ و در حقیقت یه مقدار ثابت با تایپ ‏‎String‎‏ ِه که بی‌خود و بی‌جهت یه آرگومانِ بلااستفاده لازم داره.

convList :: a -> [a]

اگه جواب، ‏‎[]‎‏ (لیستِ خالی) نباشه، یه لیستی از مقادیر یکسان میشه. طولِ لیست هم همیشه ثابت‌ه.

*

مثال‌ها از اکانتِ توئیترِ ‏‎@parametricity‎‏ گرفته شده‌اند.

۶.

پلی‌مورفیسمِ اد-هاک یا ad-hoc polymorphism (یا پلی‌مورفیسمِ محدود، constrained polymorphism) پلی‌مورفیسم‌ای‌ِه که یک یا چند محدودیت تایپکلاسی رو به یه متغیر تایپ با پلی‌مورفیسمِ پارامتریک اعمال می‌کنه. پلی‌مورفیسم اد-هاک، بجای رفتار یکسانِ تابع به ازای هر تایپِ معین، به توابع امکان رفتار متفاوت برای تایپ‌های مختلف رو میده. این اد-هاک بودن (م. یا موردی بودن) خودِش به دو چیز محدود میشه: یکی به تایپ‌های تعریف شده در تایپکلاس که متودها توش تعریف شدن، و یکی هم الزام هسکل به یکتا بودن نمونه ِ تایپکلاس‌ها در هر تایپ. به ازای هر ترکیب تایپکلاس و تایپ، مثل ‏‎Ord‎‏ و ‏‎Bool‎‏، فقط و فقط یک نمونه باید در گستره وجود داشته باشه (م. به کلام دیگه، هر تایپ فقط یک نمونه از یه تایپکلاس می‌تونه داشته باشه). چنین الزامی کار با تایپکلاس‌ها رو خیلی ساده می‌کنه. یه مثال برای رفع ابهام:

(+) :: Num a => a -> a -> a

-- Num تابع بالا با تایپکلاس
-- .پلی‌مورفیسم اد-هاک داره

c' :: a -> a -> a

-- ،ولی این تابع اینطور نیست
-- .پلی‌مورفیسم پارامتریک داره a با

۷.

ماژول یا module در زبان هسکل، واحد سازماندهی برای جمع‌آوریِ تعریف مقادیر، توابع، نوع‌داده‌ها (تایپ‌ها)، تایپکلاس‌ها، و نمونه‌های تایپکلاس‌هاست. هر وقت در هسکل از ‏‎import‎‏ استفاده می‌کنین، دارین تعاریف رو از یه ماژول وارد می‌کنین. به یه مثال از تمرین‌های فصل نگاه کنیم:

{-# LANGUAGE NoMonomorphismRestriction #-}

module DetermineTheType where
--     ^ اسم ماژول‌مون

اینجا فایل هسکل‌مون رو ماژول‌دار کردیم و اسم‌ِش رو گذاشتیم ‏‎DetermineTheType‎‏. اولِ فایل هم یه فرمان برای کامپایلر نوشتیم که محدودیت مونومورفیسم رو غیرفعال کنه. استفاده از ‏‎import‎‏ رو در مثال‌های زیر هم ببینید:

import Data.Aeson (encode)
--     ^ Data.Aeson ماژول
import Database.Persist
--     ^ Database.Persist ماژول

در مثال بالا، تابع ‏‎encode‎‏ که در ماژول ِ ‏‎Data.Aeson‎‏ تعریف شده رو به همراه هر نمونه ِ تایپکلاسی وارد کردیم. در مورد ماژول ِ ‏‎Database.Persist‎‏ هر چیزی که در اختیار گذاشته رو وارد کردیم.