۶ - ۱۳عملیات بیشتر میخوام
انواع پلیمورفیسم در تایپ سیگنچرها رو گفتیم – محدود و پارامتریک. وقتی هیچ محدودیتی روی مقادیر سطحِ جملهای نداشته باشیم یعنی میتونن هر تایپی باشن، ولی کار زیادی هم نمیشه باهاشون انجام داد. متودها و عملیاتها در تایپکلاسها قرار دارن و در نتیجه با محدودیتهای تایپکلاسی کارایی بیشتری هم بدست میاریم. اگه تایپهاتون جامعتر از جملاتتون هستن، باید تایپهاتون رو با تایپکلاسهایی که عملیاتهای مورد نیازتون رو تأمین میکنند محدود کنین. پیشتر چندتا مثال با 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 بوده، مطمئن میشیم فقط از متودهایی که قصد داشتیم در تابع استفاده میشه. این نوشدارو نیست، ولی خوبه که به این خاطر (و دلایل دیگه) گاهی اوقات از تایپهای معین دوری کنیم.