۵ - ۷اعلام تایپ برای تعاریف
بیشترِ اوقات بهتره که تایپهامون رو خودمون تعریف کنیم تا به استنتاج تایپ اتکا کنیم. اضافه کردنِ تایپ سیگنچرها به کُدتون، میتونه خودتون هم راهنمایی کنه، و باعث میشه کامپایلر هم به جایی که خطا دارین بهتر اشاره کنه. هر چقدر برنامهها بزرگتر میشن، اهمیتِ تایپ سیگنچرها هم بیشتر میشه، چون به درکِ برنامه و اینکه چه کاری قرار بوده انجام بده کمک میکنن؛ هم برای خودتون هم برای بقیه که میخوان از کُدتون استفاده کنن. تو این بخش میبینیم که چطور تایپ تعیین کنیم. اول هم با چند مثال ساده شروع میکنیم.
شاید تابعِ triple از قبل یادتون باشه. اگه تایپِش رو به کامپایلر بسپریم، چنین چیزی میگیریم:
Prelude> let triple x = x * 3
Prelude> :t triple
triple :: Num a => a -> aاینجا تابعِ triple از تابعِ (*) با تایپِ (*) :: Num a => a -> a -> a درست شده ولی به خاطر اعمال یکی از آرگومانها (عدد ۳)، یه پارامتر در تایپ سیگنچر کم شده. ولی چون تایپِ ۳ رو نمیدونه، هنوز پلیمورفیک ِه. اما اگه بخوایم ورودی و خروجیمون حتماً Integer باشن، اینجوری تعریف میکنیم:
Prelude> let triple x = x * 3 :: Integer
Prelude> :t triple
triple :: Integer -> Integerدقت کنین که بعد از این، محدودیتِ تایپکلاسی یه جملهی اضافه میشد و به همین خاطر حذف شد.
حالا تابع triple رو طوری مینویسیم که بیشتر در فایلهای منبع ممکنه ببینین:
-- تعریف تایپ
triple :: Integer -> Integer
-- تعریف تابع
triple x = x * 3در کد هسکل معمولاً همین طوری تعاریفِ سطح بالا برای تایپ و تابع، جدا از هم نوشته میشن. چنین تعاریفِ سطح بالا ای در گستره ِ تمامِ ماژول هستن.
خیلی رایج نیست، ولی اگه بخواین میتونین با let و where هم تایپها رو به صورتِ محلی تعریف کنین. یه مثال برای تخصیصِ تایپ داخلِ یه عبارتِ where:
triple x = tripleItYo x
where tripleItYo :: Integer -> Integer
tripleItYo y = y * 3دیگه لازم نیست تایپ triple رو تعیین کنیم:
Prelude> :t triple
triple :: Integer -> Integerهَمون تعریفِ تایپ در where برای تبدیلِ تایپِ triple از Num a => a -> a به Integer -> Integer کافی بود. GHC از اعمال توابع، بیانیههای فرعی، تعاریف، و تقریباً از همه جا، اطلاعاتِ تایپ رو برای استنتاج تایپ ِ بیانیهی اصلی منتشر میکنه. در این زمینه، استنتاجِ تایپ ِ هسکل واقعاً قوی عمل میکنه.
البته محدودیتهایی هم در تعریفِ تایپها وجود دارن. برای مثال اگه سعی کنیم تابعِ (+) یه String برگردونه، خطا میگیریم:
Prelude> let x = 5 + 5 :: String
No instance for (Num String) arising
from a use of ‘+’
In an equation for ‘x’: x = 5 + 5 :: Stringاین تابع نمیتونه آرگومانهای با تایپِ String قبول کنه. در این مورد "بیش از اندازه تعیین" شده، هم به خاطر تابعِ (+) که محدود به تایپهایی با Num هستند و هم به خاطر دو تا لفظ ِ عددیای که دادیم. لفظهای عددی میتونن تایپهای زیادی باشن، اما String تایپکلاسِ Num نداره، پس نمیتونن String باشن.