۶ - ۴تایپکلاس 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‎‏ رو می‌تونیم جادویی مشتق بگیریم... فقط مشتق‌گیری ِ بعضی‌هاشون یه محدودیت‌هایی دارن. مشتق‌گیری یعنی لازم نیست برای هر نوع‌داده ِ جدیدی که میسازین نمونه ِ این تایپکلاس‌ها رو دستی بنویسین. این مبحث رو در فصلِ نوع‌داده‌های جبری بیشتر بررسی می‌کنیم.