یک شرکت اتوبوسرانی در کرمانشاه سامانه اعزام دارد. برنامهای در Go که یک سطر ساعت حرکت را میخواند و روی HTTP جواب میدهد بیشتر از دو شکل ساخته نشده، و اولی روی هر فراخوانی که میتواند شکست بخورد ظاهر میشود:
daghighe, err := strconv.Atoi(field)if err != nil { return fmt.Errorf("harekat %q: %w", field, err) }
یک تابع خطایش را کنار مقدارش برمیگرداند. راه دومی برای شکست خوردن یک فراخوانی وجود ندارد: Go استثنا ندارد، پس هیچ چیز از روی بازگشتی که ننوشتهاید رد نمیشود. نتیجهاش همان چیزی است که آدمها یا دوستش دارند یا تحملش نمیکنند، یعنی اینکه if err != nil روی سطر بعدِ تقریباً هر فراخوانی میآید و مسیر خطا بهجای اینکه جای دیگری از فایل باشد کامل نوشته میشود.
چیزی که این میخرد این است که مسیر شکست بهاندازه مسیر موفقیت خواندنی است، و خواندن یک تابع Go دقیقاً میگوید وقتی هر گام خراب شود چه اتفاقی میافتد. آن %w داخل پیچیدن، خطای اصلی را داخل خطای تازه نگه میدارد، پس پیامی که به لاگ میرسد هم نام فیلد را میآورد و هم دلیل را.
شکل دوم پاکسازی است. defer f.Close() وقتی تابع برمیگردد اجرا میشود، به هر شکلی که برگردد، پس بستن کنار باز کردن نوشته میشود نه سر هر خروجی. روی تابعی با چهار بازگشت خطا، این یک سطر است بهجای چهار سطر، و دلیل این است که Go میتواند اینهمه بازگشت زودهنگام را بپردازد.
همزمانی یک کلیدواژه است، و کلیدواژه بخش جالبش نیست
go handle(req) یک گوروتین شروع میکند و فراخوان منتظر نمیماند. گوروتینها آنقدر ارزاناند که یک سرور برای هر درخواست یکی بالا میآورد بیآنکه به آن فکر کند، و زبان دقیقاً برای همین ساخته شد.
بخشی که ارزش یاد گرفتن دارد go نیست، کانال است. یک کانال هم مقدار را پاس میدهد و هم دو گوروتین را هماهنگ میکند: یک دریافت صبر میکند تا چیزی فرستاده شود، پس هماهنگی و انتقال داده یک عملاند و قفل جداگانهای برای فراموش کردن وجود ندارد. سامانه اعزام، سطرهای جدول زمانی را میان چند کارگر پخش میکند و نتیجهها را روی یک کانال جمع میکند، و هیچجای آن برنامه قفل گرفته نمیشود.
زبان عمداً کوچک است
زبان ۲۵ کلیدواژه دارد و این عدد از Go 1.0 در سال ۲۰۱۲ عوض نشده. این فخرفروشی درباره کمینهگرایی نیست، تصمیمی است با هزینه: یک کلیدواژه حلقه هست، مدیریت خطا تکراری است، و جنریک تا نسخه 1.18 در سال ۲۰۲۲ نیامد. چیزی که میخرد این است که یک خواننده میتواند تمامش را در ذهن نگه دارد، و کدی که چهل نفر نوشتهاند طوری به نظر میرسد که انگار یک نفر نوشته.
gofmt یک سبک دارد و هیچ گزینهای، پس قالببندی هیچوقت بازبینی نمیشود. و go build یک فایل اجرایی ایستا میسازد که هیچ زمان اجرایی برای نصب ندارد.
سه عدد بیربط
اندازه زبان، سن آن تعهد، و چیزی که نصب میکنید
اینها با هم مقایسه نمیشوند، و همین تنها شرطی است که سه عدد اجازه دارند کنار هم بنشینند.
- ۲۵ کلیدواژهکلیدواژههای زباناز نسخه ۱ عوض نشده، و آنقدر کم که یک خواننده همهشان را در ذهن نگه دارد
- ۲۰۱۲تعهد سازگاریکدی که آن موقع کامپایل میشد قرار است حالا هم بشود
- یک فایل اجرایی ایستاچیزهایی که برای اجرایش نصب میکنیدنه زمان اجرایی، نه مفسری، نه کتابخانه مشترکی که ماشین باید از قبل داشته باشد
هیچ ستونی رسم نشده، چون تعداد کلیدواژه و یک سال مقیاس مشترکی ندارند. گذاشتنشان در یک نمودار، رابطهای میانشان اختراع میکرد.
ابزار، و سالی که این حرف درست است
زنجیره ابزار Go یک دانلود است و رایگان و متنباز. جای سرِهم کردن جداگانه یک کامپایلر و یک قالببند و یک اجراکننده آزمون و یک ابزار وابستگی را گرفت: go build و go test و go fmt و go vet همه داخلش هستند، و به همین دلیل یک پروژه تازه Go برای شروع به هیچ فایل پیکربندی نیاز ندارد.
ماژولهای Go از نسخه 1.11 در سال ۲۰۱۸ وابستگیها را اداره میکنند و در go.sum سنجاق میکنند. جای GOPATH را گرفتند که هر پروژه را مجبور میکرد در یک پوشه از پیش تعیینشده زندگی کند، و هر دستورالعملی در اینترنت که با گفتن اینکه کدتان را کجا بگذارید شروع شود از آن قدیمیتر است.
تعهد سازگاری که با Go 1.0 در سال ۲۰۱۲ داده شد هم دانستنش میارزد: کدی که آن موقع کامپایل میشد قرار است حالا هم کامپایل شود.
و دلیلی که این زبان در ایران بیشتر از سهم جهانیاش دیده میشود همان فایل ایستاست. سروری که به مخزن بسته دسترسی ندارد، یا سروری که اجازه ندارید رویش چیزی نصب کنید، یک فایل را قبول میکند. استقرار در این زبان یعنی کپی کردن یک فایل، و همین کاری که جای دیگری یک راحتی است، اینجا گاهی تنها راه است. آگهیهای زیرساخت و دواپس ایرانی هم بیشتر از آگهیهای محصول سراغ همین میروند.
اشتباه: خطای برگشتی را شاخه بعید دانستن
تله، فراموش کردن بررسی نیست. کامپایلر و go vet بیشترش را میگیرند. تله این است که بررسی کنید و بعد کار بهدردبخوری نکنید، که شبیه وظیفهشناسی خوانده میشود و نیست: یک if err != nil که لاگ بزند و رد شود، تابع را با مقداری ادامه میدهد که نگرفته است، و خرابی سه تابع بعدتر بهشکل چیزی درمیآید که هیچ معنایی ندارد.
نسخه اول آن سامانه اعزام دقیقاً همین کار را روی فیلد ساعت حرکت کرد. ساعتی که پارس نمیشد یک سطر لاگ میزد و daghighe را صفر میگذاشت، پس اتوبوس بهجای رد شدن، ساعت دوازده شب اعزام میشد، و آن سطر لاگی که توضیحش میداد چهل هزار سطر عقبتر بود. قاعدهای که تمامش میکند: یک خطا یا همینجا رسیدگی میشود، یعنی تابع حالا میتواند صادقانه ادامه بدهد، یا برگردانده میشود. لاگ زدن گزینه سوم نیست، و لاگ بهعلاوه ادامه دادن، املای Go برای قورت دادن یک استثناست.
نیمه دیگر همین عادت، پیچیدن است. برگرداندن خام err جای آمدنش را گم میکند؛ برگرداندن fmt.Errorf با %w زنجیره را نگه میدارد، و تا وقتی به بالا برسد پیام مثل جملهای خوانده میشود درباره کاری که برنامه میخواسته انجام بدهد.
چیزی که این صفحه نپوشاند این است که سرویس آن سر کانال واقعاً چه میکند، که زیر وب است. همان فایل تکی هم دلیل این است که این زبان زیر بیشتر لایه زیرساخت نشسته، که انتشار و استقرار است، و جایش میان بقیه زبانهای برنامهنویسی است، درست کنار Rust که همان کار را با معامله سنگینتری انجام میدهد و پایتون که همان سرویس را زودتر مینویسد و سختتر مستقر میکند.