۲۸ - ۳دلیلی که این تایپ لازمه

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

بطور معمول GHC آزاده برای ارتقاء عملکرد، ترتیبِ خیلی از عملیات‌ها رو جابجا کنه، محاسبات رو به تعویق بندازه، مقادیر اسم‌دار رو به اشتراک بذاره، با درخط‌نویسی کُدها رو کپی کنه، و بهینه‌سازی‌های دیگه‌ای انجام بده. کارِ اصلی‌ای که تایپِ ‏‎IO‎‏ انجام میده اینه که بیشترِ این قابلیت‌ها رو از کار بندازه.

چی؟

جدی میگیم. بیشترِش همینه.

ترتیب و آشوب

همونطور که در فصل‌های قبل دیدیم، بطور معمول GHC می‌تونه ترتیبِ عملیات‌ها رو تغییر بده. این قابلیت توی ‏‎IO‎‏ لغو میشه (توی ‏‎ST‎‏ هم همینطور). در عوض، اجراییه‌های ‏‎IO‎‏، درونِ لانداهای تودرتو پوشونده شدن – در یه جبرِ لاندا ِ خالص، تودرتویی تنها راه برای تضمینِ تسلسل ِ اجراییه‌هاست.

دلیل اینکه می‌تونیم تضمین کنیم این کُد:

main = do
  putStr   "1"
  putStr   "2"
  putStrLn "3"

خروجیِ ۱۲۳ میده، تودرتویی ِ لاندا‌هاست. به خاطرِ نحوه‌ی تعریفِ ‏‎IO‎‏ در پشت‌پرده، امکانِ تودرتو شدنِ اجراییه‌ها و متعاقباً تسلسل ِ اونها وجود داره.

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

در واقع دلیلی که ‏‎Monad‎‏ داریم اینه که میشه باهاش شلوغی‌های لانداهای تودرتو که در زیرِ ‏‎IO‎‏ هستن رو انتزاعی کرد.