۶ - ۷تایپکلاسهای با تایپ پیشفرض
وقتی یه مقدارِ پلیمورفیک با تایپکلاسِ محدود (یا اد-هاک) دارین که نیاز هست محاسبه بشه، باید پلیمورفیسم ِش حل و یه تایپِ معین براش مشخص بشه. اون تایپِ معین باید از همهی تایپکلاسهای مورد نیاز، نمونه داشته باشه (مثلاً اگه لازمه هم Num و هم Fractional داشته باشه، نمیتونه Int باشه). معمولاً تایپهای معین یا در تایپ سیگنچر یا با استنتاج تایپ مشخص میشن، مثل وقتهایی که تایپ یه بیانیه با Num a => a اعلام شده و با گرفتنِ یه Integer، اون مقدارِ عددیِ پلیمورفیک به Integer تعیین میشه. ولی گاهی اوقات، علیالخصوص وقتهایی که در GHCi REPL کار میکنین، پیش میاد که تایپِ معیّنای برای یه مقدار پلیمورفیک تعیین نکردین. در اینجور مواقع، تایپکلاس به یه تایپِ پیشفرض تعیین میشه، و این تایپهای پیشفرض، از قبل در کتابخونهها مشخص شدن.
وقتی این رو تو REPL مینویسیم:
Prelude> 1 / 2
0.5جوابِ 0.5 اینطوری شد چون تایپش به طور پیشفرض به Double تعیین میشه. میشه با گرامر ِ :: یه تایپِ معینتر تعریف کنیم و اون تایپِ پیشفرض ِ Double رو دور بزنیم:
Prelude> 1 / 2 :: Float
0.5
Prelude> 1 / 2 :: Double
0.5
Prelude> 1 / 2 :: Rational
1 % 2گزارشِ هسکل* تایپهای پیشفرض ِ زیر رو برای محاسبات عددی مشخص میکنه:
default Num Integer
default Real Integer
default Enum Integer
default Integral Integer
default Fractional Double
default RealFrac Double
default Floating Double
default RealFloat Doubleگزارشِ هسکل استانداردهایی رو برای خود زبان و کتابخونههای استاندارد مشخص میکنه. جدیدترین نسخه تا این لحظه، گزارش هسکل ۲۰۱۰ هست که میتونین از اینجا بگیرین.
تو لیستِ بالا، Num، Real، و غیره تایپکلاسها اند، و Integer و Double تایپهای پیشفرضای هستن که اون تایپکلاسها بهشون تعیین میشن. این تعیین شدن به تایپهای پیشفرض (م. یا خلاصهتر بگیم، type defaulting) برای Fractional، باعث میشه در صورتِ تعیین نکردنِ تایپِ تابعِ (/)، تایپش از:
(/) :: Fractional a => a -> a -> aبه:
(/) :: Double -> Double -> Doubleتغییر کنه. یه مثالِ مشابه برای Integral:
div :: Integral a => a -> a -> aپیشفرض میشه به:
div :: Integer -> Integer -> Integerوقتی تایپ معین میشه، دیگه محدودیت تایپکلاسی اضافهست. از طرف دیگه، باید تایپکلاسهایی که از متغیرهای تایپتون لازم دارین مشخص کنین. اگه امکان نتیجهگیری یه تایپِ معین نبود، و هیچ قاعدهای برای تایپهای پیشفرض وجود نداشت، استفاده از مقادیرِ پلیمورفیک باعثِ گله کردنِ GHCi به خاطر مبهم بودنِ تایپ میشد.
مثالهای زیر کار میکنن چون همهی این تایپها تایپکلاسِ Num دارن:
Prelude> let x = 5 + 5 :: Int
Prelude> x
10
Prelude> let x = 5 + 5 :: Integer
Prelude> x
10
Prelude> let x = 5 + 5 :: Float
Prelude> x
10.0
Prelude> let x = 5 + 5 :: Double
Prelude> x
10.0میتونیم این تایپ رو معینتر کنیم، و فرایند کار هم تغییر چندانی نمیکنه. در این مورد، از Integer که تایپکلاس Num رو داره استفاده میکنیم:
let x = 10 :: Integer
let y = 5 :: Integer
-- اینها تایپهای تعریف شدهی این
-- توابعاند، دلیلش هم اینه که این
-- .اومدن Num توابع از تایپکلاسِ
(+) :: Num a => a -> a -> a
(*) :: Num a => a -> a -> a
(-) :: Num a => a -> a -> aهر تابعی از Num رو به x یا y اعمال کنیم، تایپش خودبهخود به Integer اختصاصی میشه:
Prelude> :t (x+)
(x+) :: Integer -> Integer
-- برای
(+) :: Num a => a -> a -> a
-- ه ِInteger تایپش a وقتی
(+) :: Integer -> Integer -> Integer
-- اعمال آرگومان اول
(x+) :: Integer -> Integer
-- اعمال دومین/آخرین آرگومان
(x+y) :: Integer
-- شد Integer جواب نهایی همبا توابعِ عمومیتر (پلیمورفیک) میشه توابعِ بخصوصتر (مونومورفیک) رو تعریف کرد:
let add = (+) :: Integer -> Integer -> Integerاین کار در خلاف جهت ممکن نیست، چرا که بعد از اختصاصی کردن به Integer، دیگه عمومیتِ Num رو از دست میدیم:
Prelude> :t id
id :: a -> a
Prelude> let numId = id :: Num a => a -> a
Prelude> let intId = numId :: Integer -> Integer
Prelude> let altNumId = intId :: Num a => a -> a
Could not deduce (a1 ~ Integer)
from the context (Num a)
bound by the inferred type of
altNumId :: Num a => a -> a
or from (Num a1)
bound by an expression type signature:
Num a1 => a1 -> a1
‘a1’ is a rigid type variable bound by
an expression type signature:
Num a1 => a1 -> a1
Expected type: a1 -> a1
Actual type: Integer -> Integer
In an equation for ‘altNumId’:
altNumId = intId :: Num a => a -> aتایپ مورد انتظار و تایپ واقعی همخونی ندارن. یادتون باشه، تایپ واقعی تایپیه که ما تأمین کردیم؛ تایپ مورد انتظار تایپیه که کامپایلر انتظار داره. اینجا تایپ واقعی معینتر از تایپ مورد انتظار ِه. تایپها رو میشه معینتر کرد، ولی نمیشه جامعتر یا پلیمورفیکتر کرد.