۱۱ - ۱۵نوع‌داده‌های گونه‌بالا

شاید یادتون باشه که کایندها رو تو این فصل گفتیم. کایندها تایپِ نوع‌سازها اند، و عمدتاً تعداد آرگومان‌هایی که می‌گیرن رو مشخص می‌کنن. کایند ِ پیش‌فرض در هسکل ‏‎*‎‏ هست. کایند سیگنچرها مثلِ تایپ سیگنچرها با همون گرامر ِ ‏‎::‎‏ و ‏‎->‎‏ نوشته میشن، اما فقط چندتا کایند وجود داره، و اکثراً هم ‏‎*‎‏ رو می‌بینید.

کایندها تا وقتی تماماً اعمال نَشَن تایپ نیستن. فقط تایپ‌ها هستن در سطح جمله‌ای عضو دارن. کایند ِ ‏‎* -> *‎‏ با یک ‏‎*‎‏ تکمیل میشه. کایند ِ ‏‎* -> * -> *‎‏ باید دو بار اعمال بشه تا یه تایپ واقعی بشه. به این میگیم تایپِ گونه‌بالا. برای مثال، لیست‌ها از نوع‌داده‌های گونه‌بالا در هسکل‌اند.

تایپ‌ها با گرفتنِ آرگومان‌های تایپی، ممکنه بطور کلی پلی‌مورفیک باشن، و به همین دلیل هم میشه در سطح نوعی اعمال بشن:

-- (a, b, c, d) همسان با
data Silly a b c d =
  MkSilly a b c d deriving Show
-- GHCi در
Prelude> :kind Silly
Silly :: * -> * -> * -> * -> *

Prelude> :kind Silly Int String
Silly Int String :: * -> * -> *

Prelude> :kind Silly Int String Bool
Silly Int String Bool :: * -> *

Prelude> :kind Silly Int String Bool String
Silly Int String Bool String :: *

-- (a, b, c, d) همسان با
Prelude> :kind (,,,)
(,,,) :: * -> * -> * -> * -> *

Prelude> :kind (Int, String, Bool, String)
(Int, String, Bool, String) :: *

حس راحتی با تایپ‌های گونه‌بالا از این لحاظ مهم ِه که میشه با آرگومان‌های تایپی یه راه جامع برای نشون دادن یه جور "سوراخ" که باید بعداً توسط مصرف‌کنندگانِ نوع‌داده پُر بِشَن رو ارائه داد. مثال زیر از کتابخونه ای به اسمِ Bloodhound که یکی از نویسنده‌های این کتاب نگهداری می‌کنه رو در نظر بگیرین.*

data EsResultFound a =
  EsResultFound {  _version :: DocVersion
                 , _source  :: a
  } deriving (Eq, Show)
*

اگه برنامه‌نویس نیستین و نمی‌دونین Elasticsearch و JSON چه چیزهایی‌اند، خیلی نگران جزئیات نباشین. Elasticsearch یه موتور جستجوِه، و JSON هم یه فرمت برای انتقال داده‌ست، به خصوص بین سرورها و برنامه‌های تحت وب.

می‌دونیم که این پاسخِ بخصوص از Elasticsearch شامل یک مقدارِ ‏‎DocVersion‎‏ هست، پس تایپ‌ش تعیین شده. از طرف دیگه، از اونجا که ساختارِ مدارکی که از Elasticsearch میگریم رو نمی‌دونیم، ‏‎_source‎‏ تایپِ ‏‎a‎‏ داره. البته در عمل باید بتونیم یه کاری با اون مقدار از تایپِ ‏‎a‎‏ انجام بدیم. معمولاً کاری که می‌خوایم باهاش انجام بدیم (طوری که اون داده رو مصرف یا استفاده می‌کنیم) یکی از نمونه‌های تایپکلاسِ ‏‎FromJSON‎‏ برای دِسریال‌سازی ِ داده ِ JSON به یه نوع‌داده ِ هسکل ِه.* ولی در هسکل، اعمال محدودیت روی نوع‌داده‌ها خیلی رایج نیست. یعنی نمی‌خوایم اون ‏‎a‎‏ ِ پلی‌مورفیک داخلِ نوع‌داده رو محدود کنیم. اکثر مواقع، متغیرهای تابع یا تابع‌هایی که داده رو پردازش می‌کنن، با تایپکلاسِ ‏‎FromJSON‎‏ در تایپ سیگنچر ِ اون تابع(ها) محدود میشن (التبه فرض کردیم که با توجه به پروژه، این تایپکلاس مورد نیاز بوده).

*

م. دِسریال‌سازی، رَوَندِ معکوسِ سریال‌سازی ِه، و میشه گفت پروسه‌ای جامع‌تر از پارس کردن به حساب میاد.

نتیجتاً، نمونه ِ تایپکلاسِ ‏‎FromJSON‎‏ برای ‏‎EsResultFound‎‏، یه نمونه از ‏‎FromJSON‎‏ برای ‏‎a‎‏ هم لازم داره:

instance (FromJSON a) =>
  FromJSON (EsResultFound a) where
    parseJSON (Object v) =
          EsResultFound
      <$> v .: "_version"
      <*> v .: "_source"
    parseJSON _ = empty

امیدواریم از این مثال متوجه شده باشین که با تماماً اعمال نکردن تایپ – گونه‌بالا نگه داشتن‌ش – جای خالی گذاشته شده تا نوع پاسخ بتونه فرق کنه. یا به کلام دیگه، اون "سوراخ" توسط مخاطب نهایی پر شه.