چاپخانهای در اصفهان هزینه هر سفارش را از روی تعداد برگ و نوع کاغذ درمیآورد و زیر یک کف هزینه نمیرود. کل برنامه پنج سازه است، و اینجا به پایتون نوشته شدهاند:
nerkh_barg = 1200یک نام را به یک مقدار میبنددif noe == "glasse": nerkh_barg = 2500انتخاب میکندfor tedad in sefareshha:روی سفارشهای روی میز تکرار میکندdef hazine(tedad): return max(tedad * nerkh_barg, 50000)تکهای از کار را نام میگذارد و جواب پس میدهدraise ValueError("سفارش بدون تعداد")عمداً متوقف میشود
یک نام، یک انتخاب، یک تکرار، یک فراخوانی و یک شکست عمدی. هر برنامهای که تا امروز خواندهاید همین پنج تاست، چیدهشده. سفارش ۸۰ برگی گلاسه ۹۶ هزار تومان درمیآید و سفارش ۴ برگی همان کف هزینه، ۵۰ هزار تومان.
همان پنج تا، با املای دیگر
در PHP هر نام یک دلار جلو دارد، بلوک را آکولاد نگه میدارد بهجای تورفتگی، و foreach ($sefareshha as $tedad) همان کاری را میکند که for tedad in sefareshha. همین. برای همین است که زبان دوم دو هفته وقت میگیرد و نه یک سال، و تمام آن دو هفته باید صرف چند جایی شود که املا فرق نمیکند و معنا فرق میکند.
یکی از آنها همین حالا در دو سطر بالا پنهان است. تابعی که چیزی برنگرداند در پایتون None میدهد و در PHP null، و هیچکدام خطا نیست: هر دو چاپ میشوند، هر دو شبیه جواباند، و برنامه چند سطر بعد سر چیز دیگری میشکند. اینکه یک نام کجا دیده میشود و کجا نه، نیمه دوم آرگومان، بازگشت، و جایی که دامنه تمام میشود است.
تابع در PHP نامِ بیرون خودش را نمیبیند
این ترجمه سطر به سطر همان برنامه است و هر سطرش درست به نظر میرسد:
$nerkh_barg = 1200;function hazine($tedad) {return max($tedad * $nerkh_barg, 50000);}
در پایتون این کار میکند، چون نام تعریفشده بیرون تابع در پایتون خوانده میشود و در PHP نه. در PHP تابع فقط آرگومانهای خودش را دارد و $nerkh_barg داخلش یک نام تازه و خالی است. PHP برای متغیر ناشناخته هشدار میدهد و برنامه با مقدار صفر ادامه میدهد، پس ضرب صفر میشود، کف هزینه برنده میشود، و هر سفارش، از چهار برگ تا هشت هزار برگ، ۵۰ هزار تومان فاکتور میخورد. هیچ سطری قرمز نمیشود. روی سرور، آن هشدار معمولاً در لاگی مینشیند که کسی باز نمیکند.
راه درستش یک کلمه است: global $nerkh_barg; نام بیرونی را به داخل تابع میآورد و هر دو زبان همان دو عدد را میدهند. راه بهترش این است که نرخ را آرگومان کنید و اصلاً به بیرون تکیه نکنید، که همان چیزی است که یک مقدار چیست و چه کارهایی رویش مجاز است از سمت دیگر میگوید.
دو مقایسه که در دو زبان یک جواب ندارند
ستون تعداد گاهی خالی است، چون اپراتور فرم را نیمهکاره رها کرده. (int)"" در PHP صفر است و int("") در پایتون ValueError میدهد. یعنی همان سفارش در یک زبان برنامه را متوقف میکند تا کسی نگاهش کند، و در زبان دیگر میشود سفارشی با صفر برگ که باز هم کف هزینه میگیرد و در جمع روز گم میشود. کدامیک را میخواهید تصمیم شماست؛ ندانستن اینکه کدام را دارید تصمیم شما نیست. این خانواده کامل خطاها موضوع شکستن با صدا، شکستن بیخطر، و فرق این دو است.
دومی به مقایسه برمیگردد. "3" == 3 در PHP درست است و در پایتون نادرست، چون PHP دو طرف را به هم نزدیک میکند و پایتون نمیکند. تا پیش از PHP 8 این نزدیک کردن تا جای عجیبی میرفت و "abc" == 0 هم درست بود؛ از PHP 8 در سال ۲۰۲۰ مقایسه رشته و عدد از سمت رشته انجام میشود و آن یکی نادرست شد. یک جا هم هست که دو زبان با هم موافقاند و کمتر کسی انتظارش را دارد: آرایه خالی در هر دو زبان نادرست است، پس if ($sefareshha) و if sefareshha: اینجا یک معنا دارند. جایی که شرطها و حلقهها از خوانا بودن درمیآیند شاخهها و حلقهها، و خوانا نگه داشتنشان است.
همان پنج تا
چاپخانه اصفهان، دو بار نوشتهشده
پنج سطر، دو املا، هرکدام یک معنا. تفاوتهایی که روی این شکل میبینید نقطهگذاریاند.
سطری که در این جدول جا نمیشود همان است که اهمیت دارد: تابع در پایتون نامِ بیرون خودش را میبیند و در PHP نمیبیند، و هیچ املایی این را جبران نمیکند.
ابزار، و سالی که این حرف درست است
Ruff در سال ۲۰۲۶ پیشفرض پایتون است، هم قالببند و هم بازرس، و جای flake8 و isort و black را گرفت که سه بار جدا روی یک فایل راه میرفتند. رایگان و متنباز است. PHPStan همین کار را از سمت معنا برای PHP میکند: فایل را بدون اجرا میخواند و نامی را که تابع استفاده میکند و هرگز نگرفته است گزارش میدهد، یعنی دقیقاً همان اشتباه دو بخش بالاتر را پیش از رسیدن به سرور میگیرد. هستهاش متنباز است. نسخه جاری خود PHP در سال ۲۰۲۶ نسخه 8.5 است و جانشین 8.4 شد.
هیچکدام از این سه درباره تله معنایی بالا نظری ندارند وقتی کد بدون نام گمشده نوشته شده باشد. یک قالببند املای پنج سازه را بررسی میکند و هرگز معنایشان را.
اشتباه: پنج سازه را پنج تکه نحو یاد گرفتن
جایی که آدمها میایستند هفته اول کار است. زبان اول را در یک دوره یاد گرفتهاند، بعد سر کار با یک کد PHP روبهرو میشوند که ده سال است کار میکند، و نتیجه میگیرند که باید برنامهنویسی را از اول بخوانند. لازم نیست. آنها پنج سازه را بهجای پنج ایده، پنج تکه نحوِ زبان اول حفظ کردهاند، پس زبان دوم بهشکل یک موضوع تازه میرسد، در حالی که فقط همان چند اختلاف معنایی تازه است.
آزمونش یک ربع طول میکشد. هزینه سفارش چاپخانه را در زبانی که بلدید بنویسید، بعد بدون نگاه کردن در زبان دیگر. هرچه در تلاش دوم فقط نقطهگذاری بود، از قبل داشتید. هرچه ماند، همان دو هفته است.
پرسشی که این صفحه باز میکند و جواب نمیدهد این است که این پنج سازه وقتی برنامه از یک صفحه بلندتر شد چطور کنار هم میمانند، و از آنجا به بعد بحث درباره نامگذاری و مرزبندی است نه درباره نحو. پیش از آن، دیدن اینکه همین پنج سطر تا رسیدن به پردازنده از چه مرحلههایی رد میشوند در کد چطور اجرا میشود است، و انتخاب زبان دوم، که به جایی که کد باید اجرا شود بستگی دارد نه به این پنج تا، زیر زبانهای برنامهنویسی نشسته است. همهشان زیر ایدههایی که با عوض شدن زبان از بین نمیروند ایستادهاند.