۲۸ - ۶انگار خلوص داره بیمعنی میشه
خیلی رایجه که در این نقطه، آدم از لغاتِ "خالصاً تابعی" یا خلوص برای توصیفِ "بیاثر بودن" استفاده کنه. ولی دقیق نیست، تعریفی هم نیست که فایده داشته باشه. ما هم سعی میکنیم یه کم توضیح بدیم تا درکی جایگزین پیدا کنین.
توضیحِ دقیق و مفهومی
از دههی ۱۹۵۰، معنای لغوی و معنای ضمنیِ لغاتِ خلوص و "خالصاً تابعی" چندباری تغییر کردن. منظوری که اصالتاً از "زبان برنامهنویسیِ تابعیِ خالص" مَدِ نظر بود، این بود که زبان از لحاظِ مفهومی فقط جبرِ لاندا باشه. برای مدتِ خیلی طولانی، زبانهای تابعیِ ناخالص متعارفتر بودن. اونها هم جبر لاندا رو اضافه میکردن، بیشتر به این خاطر که راهی برای توصیف برنامههای دستوری و اثردار هم در مفهوم ِ زبان جا داده شده باشه. قدرت هسکل در اینه که با چسبیدن به جبر لاندا، نه تنها زبانِ هستهایِ خیلی سادهتری برای توصیفِ زبان داریم، بلکه شفافیتِ مرجع در زبان هم حفظ میکنیم. برای ترتیب دادن و ایزوله کردنِ اثرات، و در عین حال حفظِ شفافیتِ مرجع، از لانداهای تودرتو (که پشتِ تجرید ِ Monad مخفی شدن) استفاده میکنیم.
شفافیت مرجع
احتمالاً میدونین شفافیت مرجع چیه، حتی اگه به اسم نشناسین. ساده بگیم، یعنی هر تابعی به ازای یک ورودی، همیشه همون یک خروجی رو میده. دقیقتر بگیم، بیانیهای شفافیتِ مرجع داره که بشه بدون تغییر در رفتارِ برنامه، با مقدارش جایگزین بشه.
یکی از چیزهایی که باعث میشه اکثراً خلوص به عنوانِ شفافیتِ مرجع، و خلوص به عنوانِ جبر لاندای خالص رو قاطی کنن، اینه که در جبر لاندای خالص، شفافیت مرجع تضمینشدهست. در نتیجه یک جبر لاندای خالص از هر دو لحاظ خالص ِه.
اشتباهی که مردم با IO میکنن اینه که اثرات رو با مفاهیم ِ برنامه ترکیب میکنن. هنوز تابعی که IO a برمیگردونه شفافیت مرجع داره، چون با آرگومانهای مشخص هر دفعه همون اجراییه ِ IO رو ایجاد میکنه! مثلاً:
module IORefTrans where
import Control.Monad (replicateM)
import System.Random (randomRIO)
gimmeShelter :: Bool -> IO [Int]
gimmeShelter True =
replicateM 10 (randomRIO (0, 10))
gimmeShelter False = pure [0]نکتهی انحرافی اینجاست که با اینکه اجرای IO [Int] با آرگومانِ True ممکنه هر بار مقادیرِ لیترالِ مختلفی درست کنه، با این حال هر دفعه به ازای یک ورودیِ مشخص، یک نتیجه میده (یعنی یه لیست از مقادیرِ تصادفی). پس شفافیتِ مرجع حفظ شده چون هنوز به ازای همون آرگومان، همون اجراییه ِ IO (یا دستورالعمل) رو برمیگردونه، همون راهِ دسترسی به یه لیستِ Int. هر ورودیِ True با این تابع، یه لیست از مقادیرِ تصادفی ِ Int برمیگردونه:
Prelude> gimmeShelter True
[1,8,7,9,10,4,2,9,3,6]
Prelude> gimmeShelter True
[10,0,7,1,10,2,4,0,9,3]
Prelude> gimmeShelter False
[0]مفهومی که سعی میکنیم برسونیم اینه که هسکل، زبانی برای محاسبهی بیانیهها و ساخت اجراییههای IO ایه که بعداً توسطِ main اجرا میشن.