۵ - ۷اعلام تایپ برای تعاریف
بیشترِ اوقات بهتره که تایپهامون رو خودمون تعریف کنیم تا به استنتاج تایپ اتکا کنیم. اضافه کردنِ تایپ سیگنچرها به کُدتون، میتونه خودتون هم راهنمایی کنه، و باعث میشه کامپایلر هم به جایی که خطا دارین بهتر اشاره کنه. هر چقدر برنامهها بزرگتر میشن، اهمیتِ تایپ سیگنچرها هم بیشتر میشه، چون به درکِ برنامه و اینکه چه کاری قرار بوده انجام بده کمک میکنن؛ هم برای خودتون هم برای بقیه که میخوان از کُدتون استفاده کنن. تو این بخش میبینیم که چطور تایپ تعیین کنیم. اول هم با چند مثال ساده شروع میکنیم.
شاید تابعِ 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
باشن.