۱۵ - ۹نمونه‌ی متفاوت، ارائه‌ی یکسان

‏‎Monoid‎‏ با بقیه‌ی تایپکلاس‌ها در هسکل از این لحاظ متفاوته که نوع‌داده‌های زیادی با بیشتر از یک مانوید ِ معتبر وجود دارن. این رو در اعداد دیدیم، جمع و ضرب هردوشون مانویدهای درستی هستن، رفتارِ متفاوتی هم دارن. چنین مواقعی بهتره با ‏‎newtype‎‏، این مانویدهای متفاوت رو از هم متمایز کنیم، مثلِ استفاده‌ای که از ‏‎Sum‎‏ و ‏‎Product‎‏ کردیم.

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

ولی برای بعضی نوع‌داده‌های دیگه، مفهومِ پیوست وضوح کمتری داره. در این مواقع، عملیات ِ مانویدی بیشتر متمرکز به پیدا کردنِ یه جور مقدار خلاصه از مقادیرِ مجموعه‌ست تا ترکیب اونها. بالاتر اشاره کردیم که مانویدها برای فولد کردن (یا بطور کلی‌تر کاتامورفیسمها) حائزِ اهمیت‌اند. شاید بهترین نگرش به ‏‎mappend‎‏ کردن، دیدنِ اون به چشمِ راهی برای متراکم کردنِ هر مجموعه‌ای از مقادیر به یک مقدار خلاصه باشه تا راهی برای ترکیب ِ مقادیر. با نگاه به نمونه‌های ‏‎Monoid‎‏ برای ‏‎Bool‎‏ شروع می‌کنیم تا منظورمون رو بهتر نشون بدیم.

مقادیرِ بولیَن دو مانوید ِ ممکن دارن – یک مانوید برای عطف و یک مانوید برای فصل ِ منطقی. مثل اعداد، اینجا هم با ‏‎newtype‎‏ این دو نمونه رو از هم تمیز میدیم. برای ‏‎Bool‎‏ این ‏‎newtype‎‏ها ‏‎All‎‏ و ‏‎Any‎‏ هستن:

Prelude> import Data.Monoid

Prelude> All True <> All True
All {getAll = True}
Prelude> All True <> All False
All {getAll = False}

Prelude> Any True <> Any False
Any {getAny = True}
Prelude> Any False <> Any False
Any {getAny = False}

‏‎All‎‏ عطف ِ منطقی رو نشون میده: اگر و فقط اگر همه‌ی مقادیری که پیوست میده ‏‎True‎‏ باشن، جواب ‏‎True‎‏ میشه. ‏‎Any‎‏ مانوید ِ فصل ِ منطقی‌ه: اگه یکی از مقادیر ‏‎True‎‏ باشه، ‏‎True‎‏ برمی‌گردونه. نگاه کردن اینها به چشم ترکیب یا mappend کردن یه جورایی عجیب‌ه، مگر اینکه به یاد داشته باشیم mappend کردن بیشتر معنی متراکم کردن یا کاهیدن رو میده تا ترکیب کردن.

تایپ ‏‎Maybe‎‏ بیشتر از دو ‏‎Monoid‎‏ ِ ممکن داره. همه‌شون رو به ترتیب می‌بینیم، اما اون دوتایی که واضح‌تر از بقیه‌اند، ‏‎First‎‏ و ‏‎Last‎‏ هستن. مثلِ فصل ِ منطقی می‌مونن، اما صراحتاً اولویتِ چپ‌ترین یا راست‌ترین موردِ موفق در یک سری از مقادیرِ ‏‎Maybe‎‏ رو بیان می‌کنن. با ‏‎Bool‎‏ فقط ‏‎True‎‏ یا ‏‎False‎‏ داریم – فرقی نداره مقادیرِ ‏‎True‎‏ یا ‏‎False‎‏ کجا باشن. اما با ‏‎Maybe‎‏، در صورتِ موفق بودنِ چند مورد از مقادیر (‏‎Nothing‎‏ نبودن) باید تصمیم بگیریم که کدوم مقدارِ ‏‎Just‎‏ رو انتخاب کنیم. ‏‎First‎‏ (به معنای اولین) و ‏‎Last‎‏ (به معنای آخرین) در واقع دو تصمیمِِ متفاوت‌اند.

‏‎First‎‏ اولین یا چپ‌ترین مقدارِ غیرِ ‏‎‎‏Nothing رو برمی‌گردونه:

Prelude> First (Just 1) `mappend` First (Just 2)
First {getFirst = Just 1}

‏‎Last‎‏ آخرین یا راست‌ترین مقدارِ غیرِ ‏‎Nothing‎‏ رو برمی‌گردونه:

Prelude> Last (Just 1) `mappend` Last (Just 2)
Last {getLast = Just 2}

حتی اگه یکی از مقادیر هم ‏‎Nothing‎‏ باشه، تا وقتی حداقل یک ‏‎Just‎‏ وجود داشته باشه، هردوشون بالاخره یه چیزی برمی‌گردونن:

Prelude> Last Nothing `mappend` Last (Just 2)
Last {getLast = Just 2}

Prelude> First Nothing `mappend` First (Just 2)
First {getFirst = Just 2}

واضحه که اگه همه‌ی مقادیر ‏‎Nothing‎‏ باشن، هردوی اینها هم ‏‎Nothing‎‏ برمی‌گردونن:

Prelude> First Nothing `mappend` First Nothing
First {getFirst = Nothing}

Prelude> Last Nothing `mappend` Last Nothing
Last {getLast = Nothing}

همه‌ی مثال‌های بالا از ‏‎newtype‎‏ استفاده کردن تا یکتا بودنِ جفت تایپ و تایپکلاس رو رعایت کنن. مثل کاری که ‏‎Sum‎‏ و ‏‎Product‎‏ کردن.

حالا به حالت سوم برای ‏‎Maybe Monoid‎‏ نگاه کنیم.