۶ - ۴تایپکلاس Eq
تساوی در هسکل با تایپکلاسِ Eq پیادهسازی شده. بعضی زبانهای برنامهنویسی، تساوی رو در ذات همهی اشیا تعریف میکنن، ولی تساوی برای همهی نوعدادهها مفهوم نداره* و هسکل هم Eq رو برای همهی تایپها تعریف نمیکنه. البته با Eq میتونیم خیلی از نوعدادهها رو برای تساوی بررسی کنیم.
مهمترینِ چنین تایپی، تایپِ تابع هست، که به خاطر دلایلی که اینجا صحبت نمیکنیم نمونه ای از Eq نداره.
Eq اینطوری تعریف شده:
Prelude> :info Eq
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Boolاولین چیزی که میگه اسمِ تایپکلاس و دو تابع پایهایِ اونه، تساوی و نامساوی؛ تایپ سیگنچرهاشون هم میده. بعد نمونههای Eq رو لیست میکنه:
-- لیست کوتاه شده
instance Eq a => Eq [a]
instance Eq Ordering
instance Eq Int
instance Eq Float
instance Eq Double
instance Eq Char
instance Eq Bool
instance (Eq a, Eq b) => Eq (a, b)
instance Eq ()
instance Eq a => Eq (Maybe a)
instance Eq Integerتایپهای عددی زیادی میبینیم، رفیقمون Bool هست، Char هست (که جای تعجب هم نداره، دیدیم که میشه مقادیرِ Char رو برای تساوی مقایسه کرد)، و توپلها هم هستن. هر وقت از مقادیرِ این تایپها استفاده کنیم، میتونیم از تابعهای استاندارد (م. == و /=) برای بررسیِ تساویشون استفاده کنیم. هر تایپی که یه نمونه از این تایپکلاس داشته باشه، متودهای این تایپکلاس رو هم در خودش داره.
چند مثال با استفاده از این تایپکلاس:
Prelude> 132 == 132
True
Prelude> 132 /= 132
False
Prelude> (1, 2) == (1, 1)
False
Prelude> (1, 1) == (1, 2)
False
Prelude> "doge" == "doge"
True
Prelude> "doge" == "doggie"
Falseتایپِ توابعِ (==) و (/=) چیز مهمی دربارهی خودِ این توابع به ما میگن:
(==) :: Eq a => a -> a -> Bool
(/=) :: Eq a => a -> a -> Boolاز روی این تایپ سیگنچرها میدونیم که هر تایپِ a که تایپکلاس Eq داره، قابلِ قبولِه. دیگه اینکه هر دوشون دو آرگومان با تایپِ یکسانِ a میگیرن و یه مقدار با تایپِ Bool برمیگردونن. میدونیم تایپِ دو آرگومان یکسانه چون a و a توی یه تایپ سیگنچر باید با هم برابر باشن.
اگه (==) رو به یه آرگومان اعمال کنیم، میبینیم که چطور روی تایپِ بقیهی آرگومانها تأثیر میذاره و در واقع "اختصاصیتر" ِشون میکنه:
(==) :: Eq a => a -> a -> Bool
-- [Char] اگه (==) رو به
-- اختصاصی کنیم String یا
(==)
:: [Char] -> [Char] -> Bool
(==) "cat"
:: [Char] -> Bool
(==) "cat" "cat"
:: Boolمیتونین تأثیرِ اعمالِ توابع به آرگومانها، روی اختصاصی شدنِ تایپشون رو تو REPL بیشتر آزمایش کنین.
حالا اگه دوتا آرگومانِ a و a یکی نباشن چی میشه؟
Prelude F M> (1, 2) == "puppies!"
Couldn't match expected type ‘(t0, t1)’
with actual type ‘[Char]’
In the second argument of
‘(==)’, namely ‘"puppies!"’
In the expression: (1, 2) == "puppies!"
In an equation for ‘it’: it = (1, 2) == "puppies!"یه کم از نزدیک به این خطای تایپ نگاه کنیم:
Couldn't match expected type ‘(t0, t1)’
with actual type ‘[Char]’این خطا یعنی [Char] ِ ما یه توپل با تایپهای t0 و t1 (تایپِ موردِ انتظار) نبود. برای آرگومان دوم (آرگومانی که ما "puppies!" رو بهش دادیم) تایپِ (t0,t1) انتظار میرفت، چون این همون تایپِ آرگومان اوله. یادتون باشه: معمولاً تایپ a از چپترین نقطه تعیین میشه و نمیتونه در تایپ سیگنچر ِ Eq a => a -> a -> Bool تغییر کنه.
اعمالِ (==) به Integer، متغیر تایپ ِ a رو به Integer مقیّد میکنه. مثل این میمونه که سیگنچر تبدیل بشه به:
Eq Integer => Integer -> Integer -> Boolولی محدودیتِ تایپکلاسی ِ Eq Integer => دیگه بیخودیِه و حذف میشه. به نمونههای تایپکلاسیِ توپل ِ دوتایی (,) نگاه کنیم:
data (,) a b = (,) a b
instance (Eq a, Eq b) => Eq (a, b)
instance (Ord a, Ord b) => Ord (a, b)
instance (Read a, Read b) => Read (a, b)
instance (Show a, Show b) => Show (a, b)در مثالهای بالاتر استفاده از نمونه ِ Eq برای تایپِ (,) رو دیدیم (1, 2) == (1, 2). نمونه ِ Eq برای (a,b) مُتکی به نمونههای Eq برای a و b ِه؛ پس تساوی ِ دو توپل با تساوی ِ مقادیرشون تعیین میشه. این مثال کار میکنه:
Prelude> (1, 'a') == (2, 'b')
Falseولی هیچ کدوم از اینا کار نمیکنن:
Prelude> (1, 2) == ('a', 'b')
Prelude> (1, 'a') == ('a', 1)مشتقگیری ِ تایپکلاس
نمونه ِ تایپکلاسهای Eq، Ord، Enum، Bounded، Read، و Show رو میتونیم جادویی مشتق بگیریم... فقط مشتقگیری ِ بعضیهاشون یه محدودیتهایی دارن. مشتقگیری یعنی لازم نیست برای هر نوعداده ِ جدیدی که میسازین نمونه ِ این تایپکلاسها رو دستی بنویسین. این مبحث رو در فصلِ نوعدادههای جبری بیشتر بررسی میکنیم.