۱۵ - ۸قوانین
یه لحظه دیگه میرسیم به اون قوانین. اول به فریادِ ما برای اهمیت به قوانینِ ریاضی گوش بسپارین:
قوانین چیزی رو که شامل یک نمونه ِ معتبر یا یک نمونه ِ معیّن از جبر (یا مجموعهی عملیاتهایی که استفاده میکنیم) هست رو محدود و مشخص میکنن. به این دلیل به قوانینی که یک نمونه ِ 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]
نکتهی مهم اینجاست که همهی اینها تضمین شدهن، بدونِ اینکه مشخص باشه با چه مانویدی کار میکنیم.