۲۶ - ۱۰الگوهای انکارپذیر و انکارناپذیر

در تطبیق الگو، دو نوع الگو وجود داره: انکارپذیر و انکارناپذیر. یه الگو ِ انکارناپذیر الگو ایه که هیچ وقت در تطبیق شکست نمی‌خوره. اما الگو ِ انکارپذیر احتمالِ شکست داره.

refutable :: Bool -> Bool
refutable True = False
refutable False = True

irrefutable :: Bool -> Bool
irrefutable x = not x

oneOfEach :: Bool -> Bool
oneOfEach True = False
oneOfEach _ = True

حواستون باشه که انکارپذیر بودن یا نبودن برای الگو ِه، نه خودِ تابع. تابعِ ‏‎refutable‎‏ انکارپذیر ِه، به خاطرِ اینکه هر حالت‌ش انکارپذیر ِه؛ هر حالت ممکنه مقداری بگیره که نتونه منطبق بشه. در مقابل، ‏‎irrefutable‎‏ الگو ِ انکارناپذیر داره؛ یعنی الگوش به انطباق بر روی یه مقدارِ بخصوص اتکا نمی‌کنه.

در موردِ ‏‎oneOfEach‎‏ (م. به معنای یکی‌ازهرکدوم)، الگو ِ اول انکارپذیر ِه، چون روی داده‌ساز ِ ‏‎True‎‏ تطبیق الگو می‌کنه. ‏‎irrefutable‎‏ و انطباق ِ دومِ ‏‎oneOfEach‎‏ انکارناپذیر اند چون لازم نیست به داخلِ مقداری که بهش اعمال شدن نگاه کنن.

البته اینکه تطبیق الگو ِ دوم در ‏‎oneOfEach‎‏ انکارناپذیر ِه، خیلی مفهومی در هسکل نداره، چون به هر حال برای تطبیق روی حالتِ اول، داده ِ ورودی باید بررسی بشه.

تابعِ ‏‎irrefutable‎‏ برای هر دو مقدارِ ‏‎Bool‎‏ کار می‌کنه، چون هیچ کدوم از اون مقادیر رو برای تطبیق مشخص نکرده. الگو ِ انکارناپذیر رو میشه الگویی دونست که هیچ وقت در تطبیق شکست نمی‌خوره. اگه یه الگو ِ انکارناپذیر قبل از یه الگو ِ انکارپذیر بیاد، الگو ِ انکارپذیر هیچ وقت احضار نمیشه.

این تابع کوچولو رو در یکی از فصل‌های قبلی دیدیم؛ می‌خوایم خیلی سریع یه چیزی رو باهاش نشون بدیم:

isItTwo :: Integer -> Bool
isItTwo 2 = True
isItTwo _ = False

با ‏‎Bool‎‏، ترتیبِ تطبیق روی ‏‎True‎‏ و ‏‎False‎‏ فرقی نداره، اما در مواردی مثلِ ‏‎isItTwo‎‏ که یک حالت بخصوص و مشخصه، و حالتِ دیگه یه حالتِ "در غیر اینصورت" یا otherwise ِه، ترتیب قطعاً مهمه. می‌تونین ترتیبِ بیانیه‌های ‏‎isItTwo‎‏ رو عوض کنین تا ببینین چی میشه، ولی احتمالاً واضحه.

الگو‌های تنبل

الگو‌های تنبل هم انکارناپذیر اند.

strictPattern :: (a, b) -> String
strictPattern (a,b) = const "Cousin It" a

lazyPattern :: (a, b) -> String
lazyPattern ~(a,b) = const "Cousin It" a

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

Prelude> strictPattern undefined
*** Exception: Prelude.undefined

Prelude> lazyPattern undefined
"Cousin It"

همونطور که اینجا می‌بینیم، تابعی که الگو ِ تنبل داشت تهی رو اجبار نکرد، چون ‏‎const‎‏ اصلاً ‏‎a‎‏ ِ توپل رو لازم نداشت. بطورِ پیش‌فرض، تهی قبل از محاسبه‌ی بدنه ِ تابع اجبار میشد، دلیل اصلی‌ش هم مصرف حافظه و عملکرد ِ قابلِ پیش‌بینی‌تَرِه.