۶ - ۱۳عملیات بیشتر می‌خوام

انواع پلی‌مورفیسم در تایپ سیگنچرها رو گفتیم – محدود و پارامتریک. وقتی هیچ محدودیتی روی مقادیر سطحِ جمله‌ای نداشته باشیم یعنی می‌تونن هر تایپی باشن، ولی کار زیادی هم نمیشه باهاشون انجام داد. متودها و عملیات‌ها در تایپکلاس‌ها قرار دارن و در نتیجه با محدودیت‌های تایپکلاسی کارایی بیشتری هم بدست میاریم. اگه تایپ‌هاتون جامع‌تر از جملات‌تون هستن، باید تایپ‌هاتون رو با تایپکلاس‌هایی که عملیات‌های مورد نیازتون رو تأمین می‌کنند محدود کنین. پیش‌تر چندتا مثال با ‏‎Integral‎‏ و ‏‎Fractional‎‏ دیدیم، اما تو این بخش جور کردن تایپ‌ها با جملات رو واضح‌تر میگیم.

مثال‌هامون رو با حالتی که تایپ‌مون جامع‌تر از جملات‌مون هستن شروع می‌کنیم:

add :: a -> a -> a
add x y = x + y

اگه این رو بارگذاری کنین، خطای زیر رو می‌بینین:

No instance for (Num a) arising from a use of ‘+’
Possible fix:
  add (Num a) to the context of
    the type signature for add :: a -> a -> a

خوشبختانه این از مواردیه که GHC میدونه مشکل دقیقاً کجاست و چاره‌ش رو پیشنهاد میده. باید یه محدودیت ِ ‏‎Num‎‏ به تایپِ ‏‎a‎‏ اضافه کنیم. اما چرا؟ چون تابع‌مون نمی‌تونه هر تایپی رو قبول کنه. یه چیزی لازم داریم که نمونه ِ ‏‎Num‎‏ داشته باشه، چراکه تابعِ ‏‎(+)‎‏ از ‏‎Num‎‏ میاد:

add :: Num a => a -> a -> a
add x y = x + y

اون محدودیت که به تایپ اضافه شد، مشکل حل شد! اگه یه متود از یه تایپکلاس دیگه استفاده کنیم چطور؟

addWeird :: Num a => a -> a -> a
addWeird x y =
  if x > 1
  then x + y
  else x

یه خطای دیگه می‌گیریم، اما اگه هول نکنیم و بر دیدِ تونلی* قَلَبه کنیم، GHC راهنمایی‌مون می‌کنه:

Could not deduce (Ord a) arising from a use of ‘>’
  from the context (Num a)
  bound by the type signature for
  addWeird :: Num a => a -> a -> a

  Possible fix:
  add (Ord a) to the context of
  the type signature for
  addWeird :: Num a => a -> a -> a
*

همه برنامه‌نویس‌ها دیدِ تونلی رو تجربه می‌کنن. آرامش خودتون رو حفظ کنین و مشکلی پیش نمیاد.

مشکل اینه که محدودیت ِ ‏‎Num‎‏ روی ‏‎a‎‏ کافی نیست. از ‏‎Num‎‏ تایپکلاسِ ‏‎Ord‎‏ نتیجه نمیشه. بنابراین، همونطور که GHC هم گفت باید یه محدودیت ِ دیگه اضافه کنیم:

addWeird :: (Ord a, Num a) => a -> a -> a
addWeird x y =
  if x > 1
  then x + y
  else x

حالا که هردو محدودیت ِ ‏‎Num‎‏ و ‏‎Ord‎‏ رو برای ‏‎a‎‏ الزام کردیم تایپچک میشه.

از تایپ‌های معین همه‌ی تایپکلاس‌هاشون نتیجه میشه

تایپ‌های ‏‎a‎‏ از چندتا مثال‌های قبلیِ این فصل رو با یه تایپِ معین عوض می‌کنیم تا این موضوع رو توضیح بدیم:

add :: Int -> Int -> Int
add x y = x + y

addWeird :: Int -> Int -> Int
addWeird x y =
  if x > 1
  then x + y
  else x

check' :: Int -> Int -> Bool
check' a a' = a == a'

اینها همه‌شون تایپچک میشن! دلیل‌ش هم تایپ ‏‎Int‎‏ ِه. ‏‎Int‎‏ همه‌ی تایپکلاس‌های ‏‎Num‎‏، ‏‎Eq‎‏، و ‏‎Ord‎‏ رو داره. پس بطور مثال لازم نیست بگیم ‏‎Ord Int => Int -> Int -> Int‎‏ چون ‏‎Ord‎‏ اینجا اطلاعاتی اضافه نمی‌کنه. یه تایپ معین، یا تایپکلاسی رو داره یا نداره – اضافه کردن محدودیت معنی نمیده. چنین تایپی همیشه تایپکلاس‌هایی که براش تأمین شدن رو نتیجه میده.

چندتا چیز رو موقعِ استفاده از تایپ‌های معین باید به خاطر سپرد. یکی از جنبه‌های مثبتِ پارامتریسیته و تایپکلاس‌ها اینه که کاری که با داده‌تون می‌خواستین انجام بدین رو صراحتاً بیان می‌کنن، و همین باعثِ خطای کمتر میشه. ‏‎Int‎‏ نوع‌داده ِ بزرگی‌ه، اعضای زیادی داره و تایپکلاس‌ها و عملیات‌های خیلی زیادی براش تعریف شدن – راحت ممکنه تابعی بنویسیم که کارِ اشتباه رو انجام بده. در مقابل اگه تابعی رو پلی‌مورفیک با تایپکلاس‌هایی که لازم داریم بنویسیم، حتی اگه منظورمون ‏‎Int‎‏ بوده، مطمئن میشیم فقط از متودهایی که قصد داشتیم در تابع استفاده میشه. این نوش‌دارو نیست، ولی خوبه که به این خاطر (و دلایل دیگه) گاهی اوقات از تایپ‌های معین دوری کنیم.