۱۱ - ۶تایپ چیه و داده چیه؟
همونطور که گفتیم، تایپها ایستا اند و در لحظهی کامپایل بررسی و مشخص میشن. قبل از زمان اجرا، تایپها معلوماند، چه از طریق تعریفِ صریح، چه از طریقِ استنتاج تایپ، و همین مسئله اونها رو ایستا میکنه. دادهها چیزهاییاند که در زمان اجرا باهاشون کار داریم.
منظور از لحظهی کامپایل دقیقاً زمانیه که برنامه توسط GHC کامپایل، یا قبل از اجرا در یه REPL (مثل GHCi) چک میشه. زمان اجرا هم همون اجرا شدن ِ برنامهست. تایپها مقادیر رو در خودشون پوشش میدن، و از همین راه میگن که چه مقداری از کدوم بخش از برنامه عبور میکنه.
ها type constructor نوعسازها یا -- compile time زمان کامپایل یا
------------------------------------- تفکیک دو مرحله
ها data constructor دادهسازها یا -- runtime زمان اجرا یاهم دادهسازها و هم نوعسازها با حرف بزرگ شروع میشن، اما در تعریفِ تایپ، سازنده ای که قبل از = میاد نوعساز ِه، و سازندههایی که بعد از = نوشته میشن دادهساز اند. دادهسازها معمولاً در تعریفِ نوعدادهها ایجاد میشن. یه نکته این که، وقتی دادهسازها آرگومان میگیرن، اون آرگومانها به تایپهای دیگهای اشاره میکنن. بنابراین هر چیزی که در تعریف نوعداده ارجاع داده میشه (م. نوشته میشه)، لزوماً توسط خودِ نوعداده ایجاد نمیشه. با یه مثال که از چندتا تایپ درست شده بیشتر توضیح میدیم.
با نوعداده ِ Price شروع میکنیم که یک نوعساز، یک دادهساز، و یک آرگومانِ تایپی در دادهساز داره:
data Price =
-- (a)
Price Integer deriving (Eq, Show)
-- (b) [1](a) نوعساز ِه. و (b) یه دادهساز ِه که یک آرگومانِ تایپی (یعنی [1]) میگیره.
مقدارِ Price تماماً به تعریفِ تایپ وابسته نیست؛ به تایپِ Integer هم وابستگی داره. اگه به هر دلیلی، Integer در گستره نباشه، مقادیرِ Price هم نمیتونیم ایجاد کنیم.
در مثال بعد دو تا تایپِ جمع، Manufacturer و Airline، که هر کدوم سه دادهساز دارن تعریف میکنیم. توی این تایپها، هر دادهساز یکی از مقادیرِ ممکن برای تایپِشون به حساب میان. هیچ کدوم آرگومانی نمیگیرن و در نتیجه بیشتر شبیهِ مقدار ثابت هستن تا سازنده، و همچنین همهشون در تعریفِ نوعداده ایجاد میشن.
data Manufacturer =
-- (c)
Mini
-- (d)
| Mazda
-- (e)
| Tata
-- (f)
deriving (Eq, Show)(c) نوعساز برای Manufacturer، و (d)، (e)، و (f) سه دادهسازهاش هستن.
data Airline =
-- (g)
PapuAir
-- (h)
| CatapultsR'Us
-- (i)
| TakeYourChancesUnited
-- (j)
deriving (Eq, Show)(g) نوعساز ِ Airline ِه، و سه دادهساز ِ (h)، (i)، و (j) داره.
حالا یه تایپ جمع که دادهسازهاش آرگومان میگیرن رو ببینیم. تایپِ Vehicle متشکل از دادهسازهای Car و Plane ِه، پس یه Vehicle یا یه Car ِه، یا یه Plane. مثلِ Price که تایپِ Integer رو به عنوانِ آرگومان میگرفت، Car و Plane هم آرگومانهایی میگیرن:
data Vehicle = Car Manufacturer Price
-- (k) (l) [2] [3]
| Plane Airline
-- (m) [4]
deriving (Eq, Show)(k) اینجا نوعساز ِه؛ دو تا دادهساز هم داره، (l) و (m). آرگومانهای تایپی با [2]، [3]، و [4] مشخص شدن. [2] و [3] آرگومانهای تایپیِ دادهساز ِ Car اند، و [4] آرگومان تایپیِ دادهساز ِ Plane. پس برای ساخت یه مقدارِ Plane، یه مقدار از تایپِ Airline لازم داریم.
همهی سازندههایی که با حروفِ انگلیسی علامتگذاری کردیم، توسطِ نوعدادهها ایجاد میشن. آرگومانهای تایپی که با اعداد نشون دادیم، قبل از تعریفها وجود داشتن؛ تعاریفِ اونها خارج از این تعاریف وجود داره، و برای اینکه بتونیم در بخشی از این تعریف ازشون استفاده کنیم، باید در گستره باشن.
عبارتِ deriving رو قبلاً هم دیده بودیم. در همهی نوعدادههای بالا، مثل اکثر مواقع در کُدِ واقعی، نمونه ای از Show رو مشتق گرفتیم. این نمونه امکان چاپِ داده به عنوانِ نوشته روی صفحه رو میده. مشتق گرفتن ِ Eq هم رایجه، این کار عملگرهای تساوی رو برای اکثر نوعدادههایی که تساوی براشون معنی میده، خودبهخود مشتق میگیره. تایپکلاسهای دیگهای هم هستن که امکانِ مشتقگیری ِ نمونههاشون باشه، این کار نیاز به نوشتنِ نمونهها برای هر نوعداده و تایپکلاس به صورت دستی رو حذف میکنه (یادآوری: یه مثال از این رو در فصل تایپکلاسها دیدین).
همونطور که دیدیم، دادهسازها ممکنه آرگومان بگیرن، که این آرگومانها تایپهای مشخصیاند، اما مقادیرِ مشخصی نیستن. در هسکلِ استاندارد، نمیتونیم مقادیرِ مشخص از تایپها رو، به عنوان آرگومانِ تایپی انتخاب کنیم. برای مثال نمیشه بگیم "Bool بدونِ مقدارِ False." اگه یه تابع، یا یه تعریفِ داده Bool میگیره، باید همهی Bool رو قبول کنه.
تمرینها: وسایل نقلیه
برای این تمرین از نوعدادههایی که بالاتر تعریف کردیم استفاده میکنیم. اگه تا الان اونها رو تو یه فایل نوشتین، میتونین ادامه بدین، اما اگه ننوشتین، لطفاً الان بنویسین. بعد هم چندتا داده ِ نمونه تعریف کنین، یا از اینها برای شروع استفاده کنین:
myCar = Car Mini (Price 14000)
urCar = Car Mazda (Price 20000)
clownCar = Car Tata (Price 7000)
doge = Plane PapuAir۱.
تایپِ myCar چیه؟
۲.
از روی تایپ سیگنچرهای زیر، توابعشون رو تعریف کنین:
isCar :: Vehicle -> Bool
isCar = undefined
-- .هست یا نه Car م. برای تشخیص اینکه
isPlane :: Vehicle -> Bool
isPlane = undefined
areCars :: [Vehicle] -> [Bool]
areCars = undefined۳.
یه تابع بنویسین که تولیدکننده ِ یه داده رو بگه:
getManu :: Vehicle -> Manufacturer
getManu = undefined۴.
حالا که Manufacturer برمیگردونه، اگه بهش یه داده ِ Plane بدیم چی میشه؟
۵.
خیلی خوب. فرض کنیم قرار شده اندازه ِ هواپیما رو هم به عنوانِ آرگومان به دادهساز ِ Plane اضافه کنین. این آرگومان رو که اضافه کردین، هرجا لازمه تغییرات مرتبط هم اعمال کنین.