۱۱ - ۱۰تایپ‌های جمع

حالا که آریتی ِ داده‌سازها رو یاد گرفتیم، می‌تونیم جبر ِ نوع‌داده‌های جبری رو تعریف کنیم. اول به تایپ جمع مثلِ ‏‎Bool‎‏ نگاه می‌کنیم:

data Bool = False | True

قبلاً گفتیم که ‏‎|‎‏ فصل منطقی، یعنی یا رو نشون میده. این معادلِ جمع در نوع‌داده‌های جبری ِه. برای پیدا کردنِ کاردینالیتی ِ تایپ‌های جمع، کاردینالیتی ِ داده‌سازهاشون رو با هم جمع می‌کنیم. ‏‎True‎‏ و ‏‎False‎‏ هیچ آرگومانی نمی‌گیرن و در نتیجه سازنده‌های پوچگانه اند و هر کدوم فقط ارزشِ ۱ برای کاردینالیتی دارن.

حالا یه کم حساب کنیم. همونطور که گفتیم، در کاردینالیتی، سازنده‌های پوچگانه ۱ اند، و تایپ‌های جمع، ‏‎+‎‏ اند:

data Bool = False | True

‏‎Bool‎‏ چندتا مقدار داره؟ دو تا داده‌ساز هستن که هر کدوم فقط یک مقدار رو ارائه میدن. با توجه به اینکه خط عمودی، جمع یا ‏‎(+)‎‏ رو نشون میده:

-- ‎‏کاردینالیتی رو نشون میده‏‎ ?? علامتِ
True | False = ??

True + False = ??

-- ‎‏اند‏‎ ۱ هر دو برابر True و False
1 + 1 == ??

در نتیجه کاردینالیتی ِ ‏‎Bool‎‏ پیدا میشه:

1 + 1 == 2

-- Bool لیست همه‌ی مقادیر ممکن برای
[True, False] -- ۲ لیست با طول

توی REPL هم میشه امتحان کرد:

Prelude> length (enumFrom False)
2

با توجه به اینها، وقتی با یه مقدارِ ‏‎Bool‎‏ کار می‌کنیم باید دو مقدارِ ممکن رو در نظر بگیریم. تایپ‌های جمع در واقع راهی برای بیانِ چند حالتِ ممکن در داخلِ یک نوع‌داده اند.

اعداد صحیح ِ ۸-بیتی علامت‌دار در هسکل، همونطور که قبلاً دیدیم با نوع‌داده ِ ‏‎Int8‎‏ که بازه‌ای از ۱۲۸- تا ۱۲۷ داره تعریف میشن. اینطوری نیست، ولی میشه این نوع‌داده رو، تایپِ جمعی که از همه‌ی اون ۲۵۶ عدد تشکیل شده فرض کرد (کاردینالیتی ِ‌۲۵۶).

تمرین‌ها: دلسوزی برای بول

۱.

با نوع‌داده ِ زیر

data BigSmall =
    Big Bool
  | Small Bool
  deriving (Eq, Show)

کاردینالیتی ِ این نوع‌داده چیه؟ راهنمایی: کاردینالیتی ِ ‏‎Bool‎‏ رو می‌دونیم. مثل حل بالا جواب بدین.

۲.

با نوع‌داده ِ زیر

-- در گستره Int8 آوردن
import Data.Int

data NumberOrBool =
    Numba Int8
  | BoolyBool Bool
  deriving (Eq, Show)

-- استفاده از پرانتز به خاطر
-- (-) تداخل گرامری بین
-- negate و تابع
let myNumba = Numba (-128)

کاردینالیتی ِ ‏‎NumberOrBool‎‏ چیه؟ اگه سعی کنین یه ‏‎Numba‎‏ با لفظ ِ عددیِ بزرگتر از ۱۲۷ درست کنین چی میشه؟ یا لفظ ِ عددی کوچکتر از (۱۲۸-)؟

اگه دقیقاً از ‏‎(-128)‎‏ استفاده کنین، یه هشدار ِ بی‌جا می‌بینین:

Prelude> let n = Numba (-128)
Literal 128 is out of the
  Int8 range -128..127
If you are trying to write a large negative
  literal, use NegativeLiterals

از اونجا که ۱۲۸- یه ‏‎Int8‎‏ ِ کاملاً معتبره، می‌شه این هشدار رو نادیده گرفت. دلیل این پیغام اینه که وقتی ‏‎(-128)‎‏ به ‏‎(negate 128)‎‏ تلخ میشه، کامپایلر می‌بینه که برای تایپِ ‏‎Int8‎‏ که حد بالاش عدد ۱۲۷ ِه، از عدد ۱۲۸ استفاده شده. کامپایلر هم قبل از اینکه منفی رو به ۱۲۸ اعمال کنه، فوراً به بزرگتر بودن ۱۲۸ از ۱۲۷ غُر میزنه. یه راه که این پیغام رو نگیرین اینه:

Prelude> let n = (-128)
Prelude> let x = Numba n

یا مثل پیشنهاد خودش از توسعه ِ ‏‎NegativeLiterals‎‏ استفاده کنین:

Prelude> :set -XNegativeLiterals
Prelude> let n = Numba (-128)

دقت کنین که استفاده از تابعِ ‏‎negate‎‏ با توسعه ِ لفظهای منفی باز هم اون هشدار رو میده.