۱۲ - ۲چگونه یاد گرفتم دست از هراس بردارم و عاشق هیچ چیز نشم
یه بار دیگه تعریفِ Maybe رو ببینیم:
data Maybe a = Nothing | Just aلازم نیست خودتون تعریف کنین، چون در Prelude هست. در هسکل هم خیلی رایجه چون برای وقتهایی که هیچ مقدار معقولی برای تایپ مورد نظرمون، a، نداریم، یه مقدارِ پیشفرض ِ Nothing در اختیارمون میذاره.*
م. لغت Nothing معنای "هیچ چیز،" لغت Just در اینجا معنای "فقط،" و خودِ Maybe هم معنای "شاید" رو میده. عنوان این بخش هم شوخی با Nothing و تیترِ یک فیلم سینمایی ِه.
تابعِ زیر یه تابعِ خیلی سادهست. تو این تابع با اعدادِ فرد کارهای زیادی میشه کرد – میشه دست نخورده بَرِشون گردوند، به نحوی متفاوت از اعدادِ زوج تغییرشون بدیم، میشه صفر برگردونیم، یا یه نشان ِ صریح خروجی بدیم که بگه به خاطر زوج نبودنش تابع هم کاری انجام نداد:
ifEvenAdd2 :: Integer -> Integer
ifEvenAdd2 n =
if even n then n+2 else ???چطور کاری کنیم بگه: "ببین، این عددی که بهم دادی زوج نبود، من هم چیزی برات ندارم؟" بجای اینکه قولِ یه جوابِ Integer بدیم، میتونیم Maybe Integer برگردونیم:
ifEvenAdd2 :: Integer -> Maybe Integer
ifEvenAdd2 n =
if even n then n+2 else Nothingهنوز کامل نیست، صحیح هم نیست. با اینکه Nothing تایپِ Maybe a داره و a هم تایپی که سازنده ِ Maybe بهش اعمال میشه رو میگیره، تایپِ n+2 هنوز Integer ِه. باید اون رو در دادهساز ِ دیگهی Maybe بپوشونیم: یعنی Just. اگه همینطور بارگذاریش کنین، این خطا رو میگیرین:
<interactive>:9:75:
Couldn't match expected type
‘Maybe Integer’
with actual type ‘Integer’
In the first argument of ‘(+)’, namely ‘n’
In the expression: n + 2اینطوری درست میشه:
ifEvenAdd2 :: Integer -> Maybe Integer
ifEvenAdd2 n =
if even n then Just (n+2) else Nothingپرانتز لازم بود، چون تابع به نزدیکترین مقدار اعمال میشه (اعمال تابع بیشترین تقدم رو داره). اگه پرانتزها رو نمیذاشتیم، کامپایلر اینطور پارس میکرد: (Just n) + 2، که غلط میبود و خطای تایپ میداد. حالا تابعمون درست شد، و هر وقت هم جوابی نداشته باشه، رُک و روراست میگه!
سازندههای هوشمند برای نوعدادهها
یه تایپِ Preson رو در نظر بگیریم که دو چیز رو نگه میداره، اسم و سن ِ شخصها. با یه تایپِ ضرب ِ ساده معرفیش میکنیم (دقت کنین که Name و Age تایپِ مستعار اند):
type Name = String
type Age = Integer
data Person = Person Name Age deriving Show
mkPerson :: Name -> Age -> Maybe Person
mkPerson name age =
| name /= "" && age >= 0 =
Just $ Person name age
| otherwise = Nothingاگه در REPL بارگذاری کنیم:
Prelude> mkPerson "John Browning" 160
Just (Person "John Browning" 160)خیلی خوب. چی میشه اگه بهش داده ِ نامساعد بدیم؟
Prelude> mkPerson "" 160
Nothing
Prelude> mkPerson "blah" 0
Just (Person "blah" 0)
Prelude> mkPerson "blah" (-9001)
Nothingبه mkPerson یه سازندهی هوشمند میگیم. فقط در صورتی داده ای از یه تایپ درست میکنه که شرایط خاصی فراهم باشن، در غیر اینصورت با یه نشان ِ صریح* میگه که اون شرایط برقرار نبودن.
م. اینجا منظور همون Nothing ِه.
این خیلی بهتر شده. اما اگه بخوایم بدونیم که اسم غلط بوده، یا سن، یا هردو، اون موقع چطور؟ شاید بخوایم به کاربر بگیم که کجا اشتباه شده. خوشبختانه برای این کار هم یه نوعداده داریم!