۱۵ - ۸قوانین

یه لحظه دیگه میرسیم به اون قوانین. اول به فریادِ ما برای اهمیت به قوانینِ ریاضی گوش بسپارین:

قوانین چیزی رو که شامل یک نمونه ِ معتبر یا یک نمونه ِ معیّن از جبر (یا مجموعه‌ی عملیاتهایی که استفاده می‌کنیم) هست رو محدود و مشخص می‌کنن. به این دلیل به قوانینی که یک نمونه ِ ‏‎Monoid‎‏ باید ازشون پیروی کُنه اهمیت میدیم چون دوست داریم هرکجا که ممکنه برنامه‌هامون صحیح باشن. اثبات‌ها برنامه‌اند، و برنامه‌ها اثبات‌اند. برنامه‌هایی برامون مهم‌اند که خوب با هم ترکیب میشن، درک‌شون آسون‌ه، و رفتارِ قابل پیش‌بینی‌ای دارن. پس تا بِشه از ریاضی دزدی می‌کنیم.

جبرها با قوانین‌شون تعریف میشن و اساساً به خاطرِ قوانین‌شون فایده دارن. قوانین وجودیتِ جبرها اند.

در کنار بقیه فوایدشون، به کمک قوانین میشه زیربنای خیلی محکمی برای برنامه‌هایی که می‌سازیم تضمین کنیم. این گارانتی یا تضمین‌ها امکانِ ترکیب ِ قابل پیش‌بینی برنامه‌ها رو در اختیار میذارن. اگه نشه برنامه‌ها رو با اطمینان با هم ترکیب کنیم، هر چیز رو مجبور میشیم از اول بنویسیم، همه چیز یکبار مصرف میشه. سنگ‌های دنیای فیزیکی هم از زمانِ هرمِ بزرگ جیزه که در دورانِ حکومتِ فرعون سنفرو در سال ۲۶۰۰ قبل از میلاد ساخته شده بود، مدام روی هم گذاشته شدن. به تشابه، اگه بخوایم توابع هم روی هم بذاریم، باید از قوانینی تبعیت کنن. سنگ‌ها یهو تبخیر نمیشن، یا ناگهانی منفجر نمیشن. اگه بشه به برنامه‌هامون هم همینطور اعتماد کنیم خوب میشه.

علاوه بر شرکت‌پذیری و همانی داشتن، قوانینِ دیگه‌ای هم از یک جبر میشه انتظار داشت، فعلاً با این مثال‌های ساده‌ شروع کردیم چون ‏‎Monoid‎‏ شروعِ خوبی برای توصیفِ این "استفاده از تایپکلاس‌ها به عنوانِ جبر" هست. بعداً مثال‌های بیشتری می‌بینیم.

نمونه‌های ‏‎Monoid‎‏ باید از قوانینِ زیر پیروی کنن:

-- همانی از چپ
mappend mempty x = x

-- همانی از راست
mappend x mempty = x

-- شرکت‌پذیری
mappend x (mappend y z) =
  mappend (mappend x y) z

mconcat = foldr mappend mempty

قانونِ همانی در عمل اینطوره:

Prelude> import Data.Monoid

-- همانی از چپ
Prelude> mappend mempty (Sum 1)
Sum {getSum = 1}

-- همانی از راست
Prelude> mappend (Sum 1) mempty
Sum {getSum = 1}

اگه اول عملگر ِ میانوند ِ ‏‎mappend‎‏، یعنی ‏‎<>‎‏ رو معرفی کنیم راحت‌تر شرکت‌پذیری‌ش رو نشون میدیم. به پرانتزگذاری در مثال‌های زیر دقت کنین:

Prelude> :t (<>)
(<>) :: Monoid m => m -> m -> m

-- شرکت‌پذیری
Prelude> (Sum 1) <> (Sum 2 <> Sum 3)
Sum {getSum = 6}

Prelude> (Sum 1 <> Sum 2) <> (Sum 3)
Sum {getSum = 6}

یه بار دیگه همه‌ی این قوانین رو برای ‏‎Monoid‎‏ ِ لیست‌ها ببینیم:

-- هست [] ‎‏معادل‏‎ mempty
-- هست (++) ‎‏معادل‏‎ mappend

-- همانی از چپ
Prelude> mappend mempty [1, 2, 3]
[1,2,3]

-- همانی از راست
Prelude> mappend [1, 2, 3] mempty
[1,2,3]

-- شرکت‌پذیری
Prelude> [1] <> ([2] <> [3])
[1,2,3]

Prelude> ([1] <> [2]) <> [3]
[1,2,3]

‏‎mconcat‎‏ هم باید همون جوابی رو بده که ‏‎foldr mappend mempty‎‏ میده:

Prelude> mconcat [[1], [2], [3]]
[1,2,3] 

Prelude> foldr mappend mempty [[1], [2], [3]]
[1,2,3]

Prelude> concat [[1], [2], [3]]
[1,2,3]

نکته‌ی مهم اینجاست که همه‌ی اینها تضمین شده‌ن، بدونِ اینکه مشخص باشه با چه مانویدی کار می‌کنیم.