۵ - ۷اعلام تایپ برای تعاریف

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

شاید تابعِ ‏‎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‎‏ باشن.