۱۱ - ۱۶لیستها پلیمورفیکاند
چه چیزی یه لیست رو پلیمورفیک میکنه؟ چطور فرمهای مختلف میگیره؟ چیزی که لیستهای هسکل رو پلیمورفیک میکنه، اینه که میتونن حاوی مقادیر از هر تایپی باشن. تا وقتی آرگومانِ تایپیِ تایپِ لیست تماماً اعمال نشه، هیچ 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
میتونه باشه، و آرگومانِ []
هر تایپی با کایند ِ *
میتونه باشه. پس شباهت دارن، اما نوعسازها رو میشه توابع در یه سطح بالاتر دونست، برای ساختنِ چیزهایی که در زمان اجرا نمیتونن وجود داشته باشن – این چیزیه که تماماً ایستا هست و ساختار تایپها رو تعریف میکنه.