۶ - ۱۲نمونه‌ها براساس تایپ‌ها خبر میشن

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

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

  • یه تایپکلاس مجموعه‌ای از توابع و یا مقادیر رو تعریف می‌کنه؛

  • تایپ‌ها نمونه‌هایی از اون تایپکلاس دارن؛

  • نمونه‌ها راه‌های استفاده از توابعِ تایپکلاس رو برای اون تایپ مشخص می‌کنن.

    این تایپکلاس فقط برای نمایش‌ه و خیلی احمقانه‌ست. لطفاً تایپکلاس‌های این شکلی ننویسین:

    class Numberish a where
      fromNumber :: Integer -> a
      toNumber :: a -> Integer
    
    
    -- ببینین data رو به چشمِ همون newtype فعلاً
    newtype Age =
      Age Integer
      deriving (Eq, Show)
    
    instance Numberish Age where
      fromNumber n = Age n
      toNumber (Age n) = n
    
    
    newtype Year =
      Year Integer
      deriving (Eq, Show)
    
    instance Numberish Year where
      fromNumber n = Year n
      toNumber (Year n) = n

    حالا فرض کنین با استفاده از این تایپکلاس و دو تایپ و نمونه‌ها یه تابع بنویسیم:

    sumNumberish :: Numberish a => a -> a -> a
    sumNumberish a a' = fromNumber summed
      where integerOfA      = toNumber a
            integerOfAPrime = toNumber a'
            summed =
              integerOfA + integerOfAPrime

    حالا یه لحظه تأمل کنیم. در تعریف تایپکلاسِ ‏‎Numberish‎‏ فقط تایپ‌ها تعریف شدن، هیچ جمله یا کُدی که بشه کامپایل و اجرا کرد توصیف نشده. کُد توی نمونه‌های ‏‎Age‎‏ و ‏‎Year‎‏ تعریف شده. حالا هسکل چطور میدونه کجا دنبال کُد بگرده؟

    Prelude> sumNumberish (Age 10) (Age 10)
    Age 20

    اینجا دید که آرگومان‌های ‏‎sumNumberish‎‏ از تایپِ ‏‎Age‎‏ بودن و از نمونه ِ ‏‎Numberish‎‏ برای ‏‎Age‎‏ استفاده کرد. با استنتاج تایپ هم میشه این رو دید:

    Prelude> :t sumNumberish
    sumNumberish :: Numberish a => a -> a -> a
    
    Prelude> :t sumNumberish (Age 10)
    sumNumberish (Age 10) :: Age -> Age

    بعد از اعمالِ اولین پارامتر به یه مقدار با تایپ ‏‎Age‎‏، هسکل می‌دونه که همه‌ی تایپ‌های ‏‎Numberish a => a‎‏ ِ دیگه هم باید ‏‎Age‎‏ باشن.

    حالا تایپکلاس و نمونه‌هاش رو تغییر میدیم تا حالتی رو ببینیم که هسکل اطلاعاتِ کافی برای شناساییِ تایپِ معین (و متعاقباً نمونه‌ای که کُد رو از روش بخونه) رو نداره.

    (این از قبلی هم بدتره. اصلاً از تایپکلاس‌ها برای تعریفِ مقادیرِ پیش‌فرض استفاده نکنین. جدی جدی. وگرنه نینجاهای هسکل پیداتون می‌کنن و تو خمیردندون‌تون دوغاب می‌ریزن.)

    class Numberish a where
      fromNumber    :: Integer -> a
      toNumber      :: a -> Integer
      defaultNumber :: a
    
    instance Numberish Age where
      fromNumber n = Age n
      toNumber (Age n) = n
      defaultNumber = Age 65
    
    instance Numberish Year where
      fromNumber n = Year n
      toNumber (Year n) = n
      defaultNumber = Year 1988

    حالا تو REPL میشه دید که گاهی اوقات هیچ راهی برای هسکل وجود نداره که بفهمه چی می‌خوایم!

    Prelude> defaultNumber
    
    No instance for (Show a0) arising
      from a use of ‘print’
    The type variable ‘a0’ is ambiguous
    -- .مبهمه ‘a0’ م. متغیرِ تایپی
     Note: There are several potential instances
      instance Show a => Show (Maybe a)
      instance Show Ordering
      instance Show Integer
      ...به اضافه‌ی ۲۴ تای دیگه

    دلیل این خطا این بود که هسکل هیچ ایده‌ای از اینکه تایپِ ‏‎defaultNumber‎‏ چی بود، به غیر از اینکه کُدش در نمونه‌های تایپکلاس ‏‎Numberish‎‏ تعریف شدن، نداشت. ولی با اینکه یه مقداره و هیچ آرگومانی نمی‌گیره یه راهی هست که منظورمون رو به هسکل برسونیم:

    Prelude> defaultNumber :: Age
    Age 65
    Prelude> defaultNumber :: Year
    Year 1988

    تایپی که انتظار دارین رو مشخص کنین و همه چیز خوب میشه! اینجا هسکل با استفاده از اون تعیینِ تایپ، تشخیص میده که ما ‏‎defaultNumber‎‏ رو از کدوم یکی نمونه‌های تایپکلاس می‌خواستیم تا خبر ِش کنه.

    چرا اینطوری تایپکلاس ننویسیم؟

    به خاطر دلایلی که سَرِ مانویدها توضیح میدیم، قانونمند بودنِ تایپکلاس‌هاتون مهمه؛ یعنی مهمه که تایپکلاس‌هاتون قواعد و قانون‌هایی برای طرزِ کارشون داشته باشن. ‏‎Numberish‎‏ یه کم... دلبخواهی و شکمی‌ه. تو هسکل راه‌های بهتری برای بیان کارایی‌ش وجود داره. اینجا توابع و مقادیر به تنهایی کفایت می‌کنند.