۱۱ - ۱۲حالت معمولی
تا اینجا مفهومِ جبر در نوعدادههای جبریِ هسکل رو بررسی کردیم و فوایدِ درکِ کاردینالیتی ِ نوعدادهها رو شناختیم. اما جبر به همینجا ختم نمیشه. همهی قواعد جبری که برای ضرب و جمع وجود دارن، برای تایپ سیستم هم صادقاند. خاصیتِ توزیعپذیری هم یکی از این قواعده. با یه مثال نحوهی کارش رو در حساب نشون میدیم:
2 * (3 + 4)
2 * (7)
14میشه از توزیع ِ ضرب روی جمع هم به همین جواب برسیم:
2 * 3 + 2 * 4
(6) + (8)
14به این میگیم "جمعِ ضربها." در حساب، حالت معمولی برای چنین بیانیهای، بعد از رسیدن به جوابه (م. یعنی ۱۴ در مثال بالا). اما اگه اون اعداد در مثالِ بالا نشوندهندهی کاردینالیتی ِ چندتا مجموعه باشن، اون موقع همون بیانیهی جمعِ ضربها حالت معمولی محسوب میشه، چون محاسبهای باقی نمونده.
خاصیتِ توزیعپذیری رو میشه تعمیم داد:
a * (b + c) -> (a * b) + (a * c)و این برای تایپهای هسکل هم صادق ِه! تایپهای ضرب روی تایپهای جمع توزیع میشن. برای اینکه بیشتر با این بازی کنیم، اول چندتا نوعداده تعریف میکنیم:
data Fiction = Fiction deriving Show
data Nonfiction = Nonfiction deriving Show
data BookType = FictionBook Fiction
| NonfictionBook Nonfiction
deriving Show
-- BookType : م. نوعِ کتابدو تایپی که هرکدوم فقط یک عضوِ پوچگانه دارن تعریف کردیم: Fiction و Nonfiction. دلایل چنین کاری شاید درجا واضح نباشه، اما به خاطر بیارین که گفتیم از یه تایپ، فقط با یکی از مقادیرش نمیشه استفاده کرد. نمیشه یه مقدارِ Bool بخواین و در تایپِ بیانیه مشخص کنین که باید همیشه True باشه – مجبورین هر دو مقدارِ Bool رو در نظر بگیرین. پس با تعریفِ Fiction و Nonfiction میتونیم انواع کتاب (که زیرش تعریف کردیم) رو تفکیک کنیم.
یه تایپ جمع به اسمِ BookType هم تعریف کردیم که سازندههاش تایپهای Fiction و Nonfiction رو به عنوان آرگومان میگیرن. به خاطر داشتنِ این مهم ِه، با اینکه اسمِ نوعسازها و دادهسازهای Fiction و Nonfiction یکیاند، اما این دو تا یک چیز نیستن. چیزی که به عنوان آرگومان به FictionBook و NonfictionBook داده میشه، نوعسازهااند. اسمهاشون رو عوض کنین و خودتون تأثیرش رو ببینین.
حالا یه تایپ مترادف به اسمِ AuthorName و یه تایپ ضرب به اسمِ Author تعریف میکنیم. تایپ مترادف واقعاً کارِ خاصی نمیکنه، فقط اینجا کمک میکنه بدونیم از کدوم String در تایپِ Author داریم استفاده میکنیم:
type AuthorName = String
data Author = Author (AuthorName, BookType)این جمعِ ضرب نیست، پس حالت معمولی هم نیست. میشه طوری محاسبهش کرد که مقادیرِ مخفی شده داخلِ تایپِ جمع ِ BookType از توش بیرون کشیده بشن. با اعمال خاصیت توزیعپذیری تایپِ Author رو در حالت معمولی بازنویسی میکنیم:
type AuthorName = String
-- اگه هر دوشون رو تو یه
-- فایل نوشتین، باید تعاریف
-- Nonfiction و Fiction قبلی از
-- .کنین یا پاک کنین comment رو
data Author =
Fiction AuthorName
| Nonfiction AuthorName
deriving (Eq, Show)ضربها روی جمعها توزیع میشن. مثل باز کردن بیانیهی a * (b + c) (که اعضای BookType معادلِ b و c میشن)، با مجزا کردن مقادیر به جمعِ ضربها رسیدیم. حالا در حالت معمولی ِه، چون تا وقتی عملیات یا محاسبهای با این تایپها انجام نشه، این سازندهها سادهتر نمیشن.
یه مثالِ دیگه از حالت معمولی رو با تایپ Expr، که در خیلی از مقالات دربارهی تایپ سیستمها و زبانهای برنامهنویسی به کار میره نشون میدیم:
data Expr =
Number Int
| Add Expr Expr
| Minus Expr
| Mult Expr Expr
| Divide Expr Exprاین جمعِ ضربها هست (تایپ جمع از تایپهای ضرب)، پس در حالت معمولی ِه: (Number Int) + Add (Expr Expr) + …
اگه بخوایم سختگیرانهتر به حالت معمولی ِ "جمعِ ضربها" برسیم، باید ضربها رو با توپل، و جمعها رو با Either نشون بدیم. پس نوعداده ِ بالا مثل زیر میشه:
type Number = Int
type Add = (Expr, Expr)
type Minus = Expr
type Mult = (Expr, Expr)
type Divide = (Expr, Expr)
type Expr =
Either Number
(Either Add
(Either Minus
(Either Mult Divide)))چنین ارائهای در جاهایی مثل برنامهنویسیِ جِنِریک یا فرابرنامهنویسی که توابع یا فولدها برای نوعدادهها نوشته میشن کاربرد داره. بعضی از این متودها در هسکل هم کاربرد دارن، اما باید با دقت زیاد به کار برن، و همیشه هم استفاده ازشون آسون نیست.
تایپِ Either رو در فصل بعد کامل توضیح میدیم.
تمرینها: رشد باغچهتون چطوره؟
۱.
با توجه به تایپ زیر
data FlowerType = Gardenia
| Daisy
| Rose
| Lilac
deriving Show
type Gardener = String
data Garden =
Garden Gardener FlowerType
deriving Showحالت معمولی (جمعِ ضرب) ِ Garden چی میشه؟