۲۸ - ۲بیراهههایی که توضیحاتِ IO میرَن
خیلی از توضیحاتِ IO غلطانداز یا سردرگماند. چندتا مثال از اون توضیحها رو دیدیم. بعضیها هم بهش میگن "IO Monad" و ظاهراً IO رو با Monad یکی میدونن. درسته که IO تایپیه که یه نمونه ِ Monad داره، ولی فقط یه Monad نیست و موندها هم فقط IO نیستن. بعضی دیگه از توضیحها طوری القا میکنن که با ورود به IO دیگه خلوص و شفافیتِ مرجع نابود میشن. بعضیها هم اصلاً چیزی از IO نمیگن چون در حقیقت، با مبهم موندنِ IO هم میشه خیلی از کارها رو انجام داد.
میخوایم چندتا راهنمایی برای سنجشِ منتقدانهی توضیحاتِ IO بگیم. اول بریم سراغ یکی از شناختهشدهترین توضیحاتی که برای IO داده شده. این توضیح سعی کرده IO رو براساس State توضیح بده.
State رو آتیش بزن!
استفاده از State برای فهموندنِ IO خیلی وسوسهبرانگیزه. یه نگاه به این جمله که از اوایلِ مستندات ِ GHC.IO گرفته شده بندازین:
IO Monad فقط یه نمونه از موند ِ ST ِه، که حالت ِش دنیای واقعی ِه.
وقتی تایپهای پشتپرده رو نگاه کنین، دلیلِ چنین توضیحاتی رو راحت میشه فهمید:
-- ghc-prim از
import GHC.Prim
import GHC.Types
newtype State s a
= State {runState :: s -> (a, s)}
-- :info IO
newtype IO a =
IO (State# RealWorld
-> (# State# RealWorld, a #))قبوله، واقعاً شبیهِ State ِه! با همهی اینها، اون قدری که اول به نظر میرسه فایده نداره.
مشکلِ این توضیح اینه که در واقع اون State#* ِ پشتپردهی IO، نه دیده میشه نه میشه باهاش تعامل داشت. State#، به اون مفهومی که از State، StateT، یا حتی ST استفاده میشه، ”State“ نیست، ولی مطمئناً اینجا رفتارِ s خیلی شبیهِ رفتارِ ST ِه.
علامتِ # نشون دهندهی یه تایپِ بَدوی ِه. این تایپها رو توی خودِ هسکل نمیشه تعریف کرد و از ماژول ِ GHC.Prim صادر میشن.
اینجا نقشِ State اینه که ترتیبِ اجراییههای IO رو به GHC بگه، و مشخص کنه چه اجراییه ِ IOیی یکتاست. اگه باز هم به مستندات ِ GHC.Prim نگاه کنیم:
State# تایپِ بَدَوی و لیفتنشده ِ حالتهاست. یک پارامترِ تایپی داره، پس میتونه State# RealWorld یا State# s باشه، که s متغیر تایپی ِه. تنها نقشِ پارامترِ تایپی اینه که ریسههای حالتِ مختلف رو از هم سوا نگه داره. با هیچ چیزی ارائه نمیشه.
RealWorld عمیقاً جادوییه. بَدِوی ِه، اما لیفتنشده نیست (در نتیجه ptrArg). هیچ وقت با مقادیرِ تایپِ RealWorld کار نمیکنیم؛ فقط در تایپ سیستم برای پارامتردار کردنِ State# به کار میره.
وقتی میگن RealWorld "با هیچ چیزی ارائه نمیشه،" منظورشون اینه که واقعاً صفر بیت حافظه مصرف میکنه. نشانههای حالت که در زیرِ تایپِ IO هستن در زمانِ کامپایل پاک میشن و هیچ سرباری ای به زمانِ اجرا اضافه نمیکنن. پس در واقع مشکلِ توضیح دادنِ IO براساسِ State این نیست که غلطه؛ مشکل اینجاست که State ای نیست که بشه تعاملِ مفهومداری، مشابهِ بقیهی تایپهای State باهاش داشت.