۱۵ - ۱۴قدرت ممکنه ضعف باشه

وقتی هسکل‌نویس‌ها میگن یه جبر قوی‌ه، منظورشون تعداد عملیات‌هایی‌ه که اون جبر ارائه میده؛ متعاقباً کارهایی هم که با هر نمونه‌ای از اون جبر میشه انجام داد (بی‌ربط به تایپ) بیشتر میشه.

نه تمایلی داریم، نه هیچ وقت همه‌ی جبرهامون رو تا جای ممکن بزرگ می‌کنیم؛ بعضی نوع‌داده‌ها هستن که کاربردهای خیلی مفیدی رو ارائه میدن، ولی نمی‌تونن همه چیز از یه جبر ِ بزرگ رو ارضا کنن. مثلاً همین تایپِ ‏‎NonEmpty‎‏. اگه برنامه‌نویسِ با تجربه‌ای هستین، خوب فکر کنین ببینین چندبار می‌خواستین لیست‌تون هیچ وقت خالی نباشه؟ برای تضمین موارد مشابه، و گویاتر کردن تایپ‌ها، از تایپ‌هایی مثل ‏‎NonEmpty‎‏ استفاده می‌کنیم.

ولی ‏‎NonEmpty‎‏ هیچ مقدارِ همانی‌ای برای عملیاتِ ترکیبی (‏‎mappend‎‏) در ‏‎Monoid‎‏ نداره. پس شرکت‌پذیری رو نگه می‌داریم و مقدار همانی، به همراه قانون‌های همانی ِ چپ و راست رو حذف می‌کنیم. چنین چیزی نیاز به ‏‎Semigroup‎‏ برای یه نوع‌داده رو معرفی می‌کنه.

واضح‌ترین موردی که نشون میده مانوید از نیم‌گروه قوی‌تره، اینه که همه‌ی عملیات‌ها و قانون‌هایی‌ه که ‏‎Semigroup‎‏ ارائه میده، زیر مجموعه‌ی عملیات‌ها و قانون‌های مانوید ِه. به عبارت دیگه مانوید یه ابرمجموعه از نیم‌گروه ِه؛ بر همین مبنا، هرچیزی که مانوید هست، نیم‌گروه هم هست. امید هست که در نسخه‌ی بعدیِ ‏‎GHC‎‏، ‏‎Semigroup‎‏ سوپرکلاس ِ ‏‎Monoid‎‏ بشه.

class Semigroup a => Monoid a where
  ...

بالاتر رابطه‌ی معکوس بین تعداد عملیات‌های مجاز برای یه تایپ و تعداد تایپ‌هایی که ارضا‌کننده هستن رو توجیه کردیم. همین رابطه‌ی معکوس رو بین تعداد عملیات‌ها و قوانینی که یه جبر تقاضا داره، و تعداد نوع‌داده‌هایی که می‌تونن نمونه‌های قانونمند از اون جبر ارائه بِدن میشه دید.

در مثال زیر، ‏‎a‎‏ می‌تونه هر چیزی باشه، اما هیچ عملیاتی هم براش نیست – فقط می‌تونیم خودش رو برگردونیم.

id :: a -> a

  • تعداد تایپ‌ها: بینهایت – سور عمومی، پس می‌تونه هر تایپی باشه.

  • تعداد عملیات‌ها: یک (اگه بشه اسم‌ش رو عملیات گذاشت)؛ ارجاع به مقدار ورودی.

    در تابعِ ‏‎inc‎‏، تایپِ ‏‎a‎‏ همه‌ی عملیات‌های ‏‎Num‎‏ رو داره، یعنی کارهای بیشتری میشه انجام داد. به این معنی هم هست که دیگه مجموعه‌ی تایپ‌هایی که می‌تونن محدودیت ِ ‏‎Num‎‏ رو ارضا کنن متناهی شده (هر تایپی که تو دنیا وجود داره نمیشه):

    inc :: Num a => a -> a

  • تعداد تایپ‌ها: هرچیزی که ‏‎Num‎‏ داره. صفر تا خیلی.

  • تعداد عملیات‌ها: ۷ تا متود ِ ‏‎Num‎‏.

    در مثال بعد، تایپ‌مون ‏‎Int‎‏ ِه، که خیلی عملیات‌های بیشتری هم نسبت به ‏‎Num‎‏ در اختیار میذاره:

    somethingInt :: Int -> Int

  • تعداد تایپ‌ها: یک، فقط ‏‎Int‎‏.

  • تعداد عملیات‌ها: خیلی بیشتر از ۷ تا. علاوه بر ‏‎Num‎‏، تایپِ ‏‎Int‎‏ از تایپکلاس‌های ‏‎Bounded‎‏، ‏‎Enum‎‏، ‏‎Eq‎‏، ‏‎Integral‎‏، ‏‎Ord‎‏، ‏‎Read‎‏، ‏‎Real‎‏، و ‏‎Show‎‏ هم نمونه داره. حتی تابع‌هایی هم میشه تعریف کرد که روی یه مقدار دلخواه از تایپ‌های معیّن تطبیقِ الگو می‌کنن و مقادیرِ دلخواه از همون تایپ رو برمی‌گردونن. پلی‌مورفیسم فقط برای استفاده‌ی دوباره از کُد کاربرد نداره؛ به دردِ بیانِ نیت از طریقِ پارامتریسیته هم می‌خوره، یعنی کمک می‌کنه هرکسی کُد رو می‌خونه، قصدِ نویسنده‌ رو هم متوجه بشه.

    هرجا ‏‎Monoid‎‏ زیادی قوی بود یا بیشتر از نیازمون بود، میشه از ‏‎Semigroup‎‏ استفاده کنیم. اگه براتون سؤال شده که چی از ‏‎Semigroup‎‏ ضعیف‌تره، جواب ماگما هست که از حذفِ شرکت‌پذیری بدست میاد. احتمال‌ش کمه تو هسکلِ روزمره لازم باشه، ولی اگه می‌خواین به بقیه پُز بدین، دونستنِ اینکه از نیم‌گروه ضعیف‌تره کاربرد داره.