۱۱ - ۱۶لیست‌ها پلی‌مورفیک‌اند

چه چیزی یه لیست رو پلی‌مورفیک می‌کنه؟ چطور فرم‌های مختلف می‌گیره؟ چیزی که لیست‌های هسکل رو پلی‌مورفیک می‌کنه، اینه که می‌تونن حاوی مقادیر از هر تایپی باشن. تا وقتی آرگومانِ تایپیِ تایپِ لیست تماماً اعمال نشه، هیچ ‏‎a‎‏ ای ندارین:

data []    a   =   []  |  a    :   [a]
--   [1]  [2]     [3]    [4]  [5]  [6]

۱.

نوع‌ساز ِ لیست، گرامر ِ خاصِ ‏‎[]‎‏ رو داره.

۲.

تنها آرگومان به ‏‎[]‎‏. این تایپِ محتوای لیست‌ه.

۳.

داده‌ساز ِ لیستِ خالی (یا nil) باز هم با گرامر ِ مخصوصِ ‏‎[]‎‏. ‏‎[]‎‏ پایانِ لیست رو نشون میده.

۴.

یک مقدارِ مجرد با تایپِ ‏‎a‎‏.

۵.

دو نقطه (‏‎:‎‏) یه داده‌ساز ِ میانوند ِه. یه ضرب از ‏‎a‎‏ از ‏‎[4]‎‏ و ‏‎[a]‎‏ از ‏‎[6]‎‏ هست.

۶.

مابقیِ لیست.

نوع‌سازها و داده‌سازهای میانوند

هر وقت به یه عملگر، اسمی بدیم که هیچ حرف و عددی توش نیست، خودبه‌خود میانوند میشه. برای مثال، همه‌ی توابعِ حساب که اسم‌شون حرف و عدد نداره میانوند اند، اما توابعی مثلِ ‏‎div‎‏ و ‏‎mod‎‏ که با حروف اسم‌گذاری شدن پیشوندی اند. تا اینجا، به غیر از سازنده ِ cons در تایپِ لیست، فقط داده‌سازهای الفباعددی رو دیدیم، اما این قاعده برای اونها هم صادق ِه.

هر اوپراتوری که با دونقطه (‏‎:‎‏) شروع میشه، باید یه نوع‌ساز یا داده‌ساز ِ میانوندی باشه. همه‌ی داده‌سازهای میانوندی باید با یه دونقطه شروع شن. نوع‌ساز ِ توابع، ‏‎(->)‎‏، تنها نوع‌ساز ِ میانوندی ِه که با دونقطه شروع نمیشه. یه استثناء دیگه اینکه نمی‌تونن ‏‎::‎‏ باشن، چون برای اعلام تایپ گرفته شده.

در مثال زیر، تایپ لیست رو بدون سازنده ِ میانوند تعریف می‌کنیم:

-- همون تایپ که با
-- گرامر متفاوت تعریف شده
data List  a  = Nil | Cons  a   (List a)
--   [1]  [2]   [3]   [5]  [4]  [6]

۱.

نوع‌ساز ِ ‏‎List‎‏.

۲.

پارامتر تایپیِ ‏‎a‎‏ برای ‏‎List‎‏.

۳.

مقدارِ ‏‎Nil‎‏ یا لیست خالی، که نشون دهنده‌ی پایانِ لیست هم هست.

۴.

یه مقدارِ مجرد از تایپِ ‏‎a‎‏ در ضرب ِ ‏‎Cons‎‏.

۵.

سازنده ِ ‏‎Cons‎‏، ضرب ِ ‏‎a‎‏ و ‏‎List a‎‏.

۶.

مابقیِ لیست.

چطور از این تایپِ ‏‎List‎‏ استفاده کنیم؟

Prelude> let nil = Nil
Prelude> :t nil
nil :: List a

پارامترِ تایپی اعمال نشده، چون ‏‎Nil‎‏ به تنهایی چیزی از محتویاتِ ‏‎List‎‏ به استنتاج تایپ نمیگه. اما اگه اطلاعات بیشتری بدیم، یه تایپِ معیّن هم به ‏‎a‎‏ داده میشه:

Prelude> let oneItem = (Cons "woohoo!" Nil)
Prleude> :t oneItem
oneItem :: List [Char]

کایند ِ تایپ‌های لیست‌مون چطورن؟

Prelude> :kind List
List :: * -> *
Prelude> :kind []
[] :: * -> *

Prelude> :kind List Int
List Int :: *
Prelude> :kind [Int]
[Int] :: *

نوع‌ساز ِ لیستِ ‏‎[]‎‏ قبل از اعمال به آرگومان‌های تایپی‌ش، شباهت‌هایی با تابعی مثلِ ‏‎‎‏not قبل از اعمال به آرگومان‌هاش داره:

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

Prelude> :k []
[] :: * -> *
Prelude> :k [Int]
[Int] :: *

تفاوت اینجاست که آرگومانِ ‏‎not‎‏ هر مقداری با تایپِ ‏‎Bool‎‏ می‌تونه باشه، و آرگومانِ ‏‎[]‎‏ هر تایپی با کایند ِ ‏‎*‎‏ می‌تونه باشه. پس شباهت دارن، اما نوع‌سازها رو میشه توابع در یه سطح بالاتر دونست، برای ساختنِ چیزهایی که در زمان اجرا نمی‌تونن وجود داشته باشن – این چیزیه که تماماً ایستا هست و ساختار تایپ‌ها رو تعریف می‌کنه.