۲ - ۷تعریف مقادیر

از اونجا که GHCi یه فایل منبع رو یکباره و کامل بارگذاری می‌کنه، ترتیب تعاریف توی فایل اهمیتی نداره، GHCi همه‌ی مقادیر تعریف شده رو می‌شناسه. ولی تو REPL که یکی یکی تعاریف رو وارد می‌کنیم، ترتیب مهمه.

برای مثال، می‌تونیم یک سری از تعاریف رو توی REPL اینطوری وارد کنیم:

Prelude> let y = 10
Prelude> let x = 10 * 5 + y
Prelude> let myResult = x * 5

حالا اگه اسم اون مقادیر رو وارد کنیم، مقدارشون رو می‌بینیم:

Prelude> x
60
Prelude> y
10
Prelude> myResult
300

ببینیم چطوری همین مقادیر رو تو یه فایل به اسم ‏‎learn.hs‎‏ وارد کنیم. اول از همه اسمِ ماژول رو می‌نویسیم که بعداً با همین اسم بتونیم توی پروژه وارد ِش کنیم (فعلاً سراغ پروژه به این بزرگی که چندتا ماژول داشته باشه نمیریم، ولی خوبه که از الان به نوشتنِ اسم ماژول‌ها عادت کنین):

-- learn.hs

module Learn where

x = 10 * 5 + y

myResult = x * 5

y = 10

اسم ماژول‌ها با حروف بزرگ شروع میشه. برای اسم متغیرها، شُتُری نوشتیم: حرف اول کوچیکه، ولی هر جا به لغتِ بعد رسیدیم، با حرف بزرگ شروع‌ش کردیم. این کار، خوندنِ اسم‌ها رو راحت‌تر می‌کنه.

عیب‌یابی

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

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

اکثر مواقع در فایل‌های هسکل، توگذاری جای علائم گرامری مثل آکولاد، نقطه ویرگول، و پرانتز رو می‌گیره. قاعده‌ی کلی اینه که اگه کُدی بخشی از یه بیانیه‌ی دیگه‌ست، باید زیر اون بیانیه توگذاری بشه، حتی اگه اون بیانیه در چپ‌ترین ستونِ متن نباشه. همچنین، اون بخشهایی از کد که با هم دسته‌بندی میشن، باید همه‌شون به یه اندازه توگذاری بشن. برای مثال، در یه بلوکِ کد که با ‏‎let‎‏ یا ‏‎do‎‏ شروع شده، چنین توگذاری ای صحیحه:

let 
  x = 3
  y = 4

-- یا

let x = 3
    y = 4

البته این کد فقط در صورتی توی یه فایل صحیحه، که زیرمجموعه‌ی یک تعریفِ سطح بالاتر باشن (م. مثلاً یه تابع).

دقت کنید اون دو تا تعریف‌هایی که جزء یک بیانیه‌اند، از یک ستون شروع شدن. اینطوری نوشتن درست نیست:

let x = 3
 y = 4

-- یا

let
 x = 3
  y = 4

اگه بیانیه‌تون از چند بخش تشکیل شده، مثل زیر توگذاری کنین:

foo x =
    let y = x * 4
        z = x ^ 2
    in 2 * y * z

می‌بینید که تعریف‌های ‏‎y‎‏ و ‏‎z‎‏ از یک ستون شروع شدن، ‏‎let‎‏ و ‏‎in‎‏ هم در یه راستا اند. در طولِ کتاب به طریقه‌ی توگذاری ِ ما دقت کنین. خیلی پیش میاد که یه توگذاری ِ نابجا کد رو خراب کنه. اگه کد رو از جایی کپی کنین، احتمال توگذاری ِ اشتباه خیلی میره بالا.

اگه اشتباهی وسط یه تعریف برین خط بعد، طوری که ادامه‌ی بیانیه از اول خط شروع بشه:

module Learn where
--تعریف ماژول در ابتدای فایل 

x = 10
* 5 + y

myResult = x * 5

y = 10

یه پیغام خطا مثل این میاد:

Prelude> :l code/learn.hs
[1 of 1] Compiling Learn

code/learn.hs:10:1:
  parse error on input ‘*’
Failed, modules loaded: none.

اولین خطِ پیغام خطا، جایی که اشتباه پیش اومده رو میگه: ‏‎code/learn.hs:10:1:‎‏ به خطا در خطِ ۱۰ و ستونِ ۱ از فایل ‏‎learn.hs‎‏ در پوشه‌ی ‏‎code‎‏ اشاره می‌کنه. پیدا کردنِ عاملِ error با چنین پیغامی خیلی آسون‌تر میشه. البته واضحه که خط و ستون خطای شما ممکنه جاهای دیگه‌ای باشن.

برای تصحیح این خطا، یا می‌تونین همه‌ش رو توی یه خط بنویسین:

x = 10 * 5 + y

یا دقت کنین اگه وسط بیانیه رفتین خط بعد، حتماً اون خط رو حداقل با یک فاصله از اون جایی که خط قبل شروع شده، بنویسین (هر دو مثالِ زیر کار می‌کنن):

x = 10
 * 5 + y

-- یا

x = 10
    * 5 + y

دومی یه کم بهتره. در کل سعی کنید فقط وقتی وسط جمله برین خط بعد که از ۱۰۰ ستون رد شدین.

خطای دیگه‌ای که ممکه رخ بده، اینه که یه تعریف رو از اولِ خط (سمت چپ) شروع نکنین:

-- learn.hs

module Learn where

 x = 10 * 5 + y

myResult = x * 5

y = 10

اون فاصله رو قبل از ‏‎x‎‏ می‌بینین؟ همون باعثِ چنین پیغام خطایی شده:

Prelude> :l code/learn.hs
[1 of 1] Compiling Learn

code/learn.hs:11:1:
  parse error on input ‘myResult’
Failed, modules loaded: none.

یه کم گیج کننده‌ست... پیغام به ‏‎myResult‎‏ اشاره کرده، ولی خطا جای دیگه‌اییه. خطا فقط به خاطر یه فاصله‌ی اضافه‌ست؛ ولی همه‌ی تعاریف در ماژول باید از یک ستون شروع شن، و اون ستون توسط اولین تعریف در ماژول تعیین میشه. در این مثال، دلیل اینکه پیغامِ خطا به جای دیگه‌ای اشاره می کنه، اینه که ستونی که تعاریف باید ازش شروع بشن رو همون ستونی میدونه که تعریفِ ‏‎x‎‏ ازش شروع شده. پس از دیدِ کامپایلر*، تعریفِ ‏‎myResult‎‏ یک ستون زود شروع شده.

*

م. کامپایلر یک واسط نرم‌افزاریه که یک زبان برنامه‌نویسی رو به یک زبان برنامه‌نویسی دیگه تبدیل می‌کنه.

یک راه اینه که تعاریف ‏‎y‎‏ و ‏‎myResult‎‏ رو یه ستون بیاریم جلو:

-- learn.hs
module Learn where

 x = 10 * 5 + y

 myResult = x * 5

 y = 10

ولی منظره‌ی خوبی نداره، و کدنویسیِ استاندارد هسکل نیست. تقریباً هیچ وقت پیش نمیاد که لازم بشه کلِ کد رو توگذاری کنین، ولی کمک می‌کنه رَوشِ کارِ کامپایلر رو بهتر بشناسین. نتیجه‌ای که می‌گیریم اینه که هر وقت چنین پیغام خطایی دیدین، مطمئن شین که اولین تعریف‌تون از چپ‌ترین نقطه‌ی خط شروع میشه، بعد دنبال خطاهای دیگه باشین.

یه اشتباه دیگه که ممکنه پیش بیاد، اینه که موقعِ نوشتن کامِنت (م. یا توضیحاتی که کامپایلر نمی‌خونه)، یکی از خطِ فاصله‌های اولش رو (‏‎--‎‏) نذارین.

پس این کد:

- learn.hs

module Learn where

x = 10 * 5 + y

myResult = x * 5

y = 10

چنین خطایی میده:

code/learn.hs:7:1:
  parse error on input ‘module’
Failed, modules loaded: none.

باز هم خطای پارس، جای دیگه‌ای اشاره شده. اشکال در واقع کم بودنِ یک خطِ تیره اولِ کُد ِه. کامنت‌های یک خطی در هسکل با دو تا خط تیره (‏‎--‎‏) شروع میشن.

حالا ببینیم چه طوری از کُدی که توی فایل نوشته شده در GHCi استفاده کنیم. با فرض اینکه REPL رو از همون پوشه‌ای که ‏‎learn.hs‎‏ توشه اجرا کنیم، کارهای زیر رو میشه انجام داد:

Prelude> :l learn.hs
[1 of 1] Compiling Learn
Ok, modules loaded: Learn.
Prelude> x
60
Prelude> y
10
Prelude> myResult
300

تمرین‌ها: بیمار رو دریاب

مثال‌های زیر خرابن و کامپایل نمیشن. دوتا اولی برای نوشتنِ مستقیم توی REPL طراحی شدن؛ آخری توی یه فایل‌ه. ایرادات‌شون رو پیدا کنید و کاری کنین کامپایل بشن.

۱.

let area x = 3. 14 * (x * x)

۲.

let double x = b * 2

۳.

x = 7
 y = 10
f = x + y