۴ - ۳آناتومی تعریف داده

تعریفِ داده راهی برای تعریفِ تایپ‌هاست.

نوع‌ساز یا type constructor، اسمِ تایپ‌ه و با حروفِ بزرگ شروع میشه. وقتی تایپ سیگنچرها رو می‌خونین یا می‌نویسین (در سطحِ نوعی ِکد هستین)، از اسمِ تایپ‌ها، یا نوع‌سازها استفاده می‌کنین.

داده‌سازها یا data constructors مقادیری‌اند که داخلِ یک تایپ تعریف میشن. همون مقادیری‌اند که در سطح جمله‌ای ِکد ظاهر میشن، و نه سطح نوعی. منظور از سطح جمله‌ای، مقادیری‌اند که در خودِ کد ظاهر میشن، و یا کد به اونها حساب میشه.

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

-- Bool تعریف
data Bool = False | True
--    [1]    [2] [3] [4] 
  1. نوع‌ساز برای تایپِ Bool. اسمِ تایپ که در تایپ سیگنچرها هم دیده میشه.

  2. داده‌ساز برای مقدار False.

  3. خط عمودی | که تعیین کننده‌ی نوعِ جمع یا فصل منطقی (یا) ِه.

  4. داده‌ساز برای مقدار True.

به کلِ اون عبارت میگیم تعریف داده.تعریف داده‌ها می‌تونن شکل‌های دیگه هم داشته باشن – بعضی تایپ‌ها عطفِ منطقی(و) دارن (برخلافِ این مثال که فصلِ منطقی ِه)، بعضی نوع‌سازها و داده‌سازها هم آرگومان دارن. با وجودِ این تفاوت‌ها، همه‌شون نقاطِ مشترکی هم دارن: یکی کلیدواژه‌ی data در اولِ تعریفه که بعد از اون،نوع‌ساز (یا همون اسمِ تایپ) نوشته میشه، علامت مساوی که نشون میده این یه تعریف‌ه، و آخر هم داده‌سازها (یا اسم مقادیری که در سطحِ جمله‌ای کد قرار می‌گیرن) که بعد از مساوی نوشته میشن.

شما می‌تونین تعریف یک نوع‌داده ِاز پیش تعریف شده رو با دستور info: در GHCi ببینین:

Prelude> :info Bool
data Bool = False | True

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

Prelude> :t not
not :: Bool -> Bool

ولی موقعِ استفاده از این تابع، از داده‌سازها (یا خودِ مقادیر) استفاده می‌کنیم:

Prelude> not True
False

بیانیه‌مون هم به یه داده‌ساز یا مقدارِ دیگه ساده میشه – که اینجا تنها داده‌سازِ دیگه‌ی همون تایپ رو نتیجه میده.

تمرین‌ها: نوسان خُلقی

با نوع‌داده‌ی زیر، سؤال‌های زیر رو جواب بدین:

data Mood = Blah | Woot deriving Show

هنوز اون تیکه‌ی deriving Show رو توضیح ندادیم. فعلاً فقط در همین حد توضیح میدیم که مشتق کردنِ Show باعث میشه مقادیرِ تایپ‌هایی که خودتون تعریف می‌کنین قابلِ چاپ بشن. به تایپکلاس‌ها که برسیم، بیشتر توضیح میدیم.

۱.

نوع‌ساز، یا اسم این تایپ چیه؟

۲.

اگه تابعی ورودیش یه مقدارِ Mood باشه، از چه مقادیری میشه استفاده کرد؟

۳.

میخوایم یه تابعِ changeMood بنویسیم که خُلق ِکریس رو فوراً عوض کنه. باید کاری شبیهِ تابع not انجام بده: با یک مقدارِ ورودی، تنها ورودی دیگه‌ی همون تایپ رو برگردونه. ما این تایپ سیگنچر رو براش نوشتیم:

changeMood :: Mood -> Woot

مشکلش چیه؟

۴.

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

changeMood Mood = Woot
changeMood    _ = Blah

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

۵.

همه‌ی چیزهایی که بالاتر نوشتین رو تو یه فایل هم بنویسین –نوع‌داده (به همراهِ اون deriving Show)، تایپ سیگنچرِ اصلاح شده، و تابع تکمیل شده. فایل رو در GHCiبارگذاری کنین تا از درست بودنِ همه چیز مطمئن بشین.