Go भाषा: हमने क्या सही किया और क्या गलत किया
(commandcenter.blogspot.com)- Go को 10 नवंबर 2009 को open source के रूप में जारी किए जाने के बाद 14 वर्षों में, इसका विकास नई भाषा बनाने से अधिक production software को आसानी से बनाने के तरीके पर केंद्रित रहा
- औपचारिक specification, कई compiler implementations, आसान cross compilation, Go 1.0 के बाद compatibility guarantee, standard library और gofmt ने adoption को मजबूत किया
- सर्वर software को संरचित करने के लिए concurrency इसकी ताकत थी, लेकिन शुरुआती दौर में parallelism से इसका अंतर और सही उपयोग के मामले पर्याप्त रूप से न समझाए जाने से भ्रम पैदा हुआ
- interfaces library, testing, dependency management और composition-केंद्रित design की नींव बने, जबकि generics की अनुपस्थिति ने अगले 10 से अधिक वर्षों तक polymorphic design को कठिन बनाया
- package management, documentation और community collaboration में काफी trial and error रहा, लेकिन Go का लक्ष्य ऐसा वातावरण बनाना था जहाँ कोड लेखक और समय बदलने पर भी समान दिखे और लगातार build होता रहे
Go का लक्ष्य: भाषा से अधिक software development का तरीका
- 10 नवंबर 2023, Go के open source project के रूप में जारी होने की 14वीं वर्षगांठ थी
- 2009 में सार्वजनिक किए जाने के समय Ken Thompson, Robert Griesemer, Russ Cox, Ian Taylor, Adam Langley, Jini Kim आदि ने project की घोषणा देखी थी
- Go का मूल लक्ष्य नई programming language बनाना नहीं, बल्कि बेहतर तरीके से high-quality software लिखना था
- उस समय bottleneck language features से अधिक Google में आधुनिक server software बनाने की जटिलता में था
- dependency control
- बड़ी teams और बदलती workforce structure
- maintainability
- efficient testing
- multicore CPU और networking का उपयोग
- Go आज भी ऐसा project माना जाता है जो production software बनाना अधिक आसान और productive बनाता है
Gopher mascot से बनी community की पहचान
- Go gopher, Go की शुरुआती सफलता के प्रमुख कारणों में से एक था
- Renee French द्वारा बनाया गया gopher, Go programmers के साझा प्रतीक और community के झंडे जैसा बन गया
- GopherCon जैसे conference नामों की तरह, gopher ने project में technical excellence और fun, दोनों वाली भावना बनाई
- इसे Creative Commons Attribution license के तहत जारी करने के फैसले के फायदे और नुकसान दोनों थे
- remix और variations को बढ़ावा मिला, जिससे community spirit मजबूत हुई
- attribution शर्त के कारण विवाद हुए, या Renee French द्वारा न बनाए गए कामों में भी गलत credit जुड़ गया
- mascot की मूल भावना को बनाए रखते हुए उसका प्रबंधन करना अब भी एक कठिन चुनौती है
वे तकनीकी निर्णय जो Go ने सही लिए
- औपचारिक specification पहले लिखी गई, ताकि compiler implementations के व्यवहार को स्थिर किया जा सके और कई implementations एक ही व्यवहार पर सहमत हो सकें
- यह समझ थी कि केवल एक compiler ही specification नहीं बन सकता
- पहला specification draft Sydney Darling Harbour की एक इमारत की 18वीं मंजिल पर लिखा गया था
- कई compiler implementations ने भाषा और specification को बेहतर बनाने में मदद की
- Ian Taylor ने 7 जून 2008 को Go draft specification पढ़कर gcc frontend बनाने की बात ईमेल में लिखी थी
- इसके बाद भी कई compatible implementations आए
- cross compilation को आसान बनाने से developers अपनी पसंद के platform पर काम कर सके और ज़रूरत के platform पर deploy कर सके
- Go 1.0 में भाषा को व्यवस्थित करने के बाद compatibility guarantee लाने का फैसला Go के adoption पर बड़ा असर डालने वाला साबित हुआ
- मजबूत compatibility बनाए रखने की लागत है, लेकिन इससे यह चिंता कम होती है कि नया Go release project को तोड़ देगा
- standard library ने 21वीं सदी के server code के लिए ज़रूरी अधिकांश सुविधाएँ दीं और community को एक ही toolkit पर काम करने दिया
- शुरुआती समय में Go code install करने के लिए दूसरी जगहें नहीं थीं, इसलिए library कुछ बड़ी भी हो गई
- इससे variants की भरमार कम हुई और community को एकजुट रखने में मदद मिली
tools और gofmt से बना development experience
- Go को ऐसी भाषा के रूप में डिज़ाइन किया गया जिसे parse करना आसान हो, ताकि tools बनाना सरल हो
- शुरुआत में IDE को ज़रूरी माना गया था, लेकिन tooling आसान होने से समय के साथ IDEs ने Go को support किया और gopls भी आया
- compiler के साथ automatic testing, coverage, code vetting जैसे सहायक tools दिए गए
- go command ने पूरे build process को एकीकृत किया, और कई projects Go code के build और maintenance के लिए इसी command से अधिकांश काम कर सके
- gofmt सिर्फ Go ही नहीं, पूरी programming community को प्रभावित करने वाला tool बन गया
- Robert Griesemer ने शुरुआत से ही gofmt बनाने पर ज़ोर दिया था
- gofmt से पहले automatic formatters की quality इतनी अच्छी नहीं थी कि वे व्यापक रूप से इस्तेमाल हों
- whitespace और line breaks पर बहस में लगने वाला समय घट गया
- gofmt का internal हिस्सा library बन गया, जिससे AST को parse, modify और फिर human-readable output में बदला जा सका
- इससे simplifier, analyzer और code coverage tools जैसे दूसरे tools भी संभव हुए
concurrency: शक्तिशाली, लेकिन समझाने में देर हुई
- 2002 के समय Google software में threads से लगभग बचा जाता था, और John Ousterhout के “threads were bad” दृष्टिकोण का हवाला देकर उनके उपयोग को हतोत्साहित किया जाता था
- Go की शुरुआत इस अनुभव से हुई कि pthread जैसे low-level packages की तुलना में concurrency को बेहतर तरीके से इस्तेमाल किया जा सकता है
- programming जगत में concurrency को शक्तिशाली tool के रूप में स्वीकार कराने में Go ने महत्वपूर्ण भूमिका निभाई
- खासकर multicore और network environments में इसका प्रभाव बड़ा था
- आज अधिकांश mainstream languages में concurrency support मौजूद है
- Go ने coroutine, task या thread जैसे नामों की बजाय goroutine जैसा नया शब्द गढ़ा
- async/await, pthread की तुलना में बड़ा सुधार है, लेकिन goroutine, channel और select की तुलना में यह implementers के लिए आसान और छोटा होते हुए भी कुछ जटिलता programmer पर वापस डाल सकता है
- Bob Nystrom की “What Color is Your Function?” समस्या इसका उदाहरण है
- Go का CSP model procedural languages के लिए अच्छी तरह उपयुक्त है, लेकिन उसे सही तरह लागू करने के लिए runtime की जटिलता चाहिए
- एक ही environment में concurrency के कई implementations समस्या बन सकते हैं, इसलिए Go ने इसे library की बजाय language में शामिल किया
concurrency में की गई दो गलतियाँ
- Go team शुरुआती दौर में यह पर्याप्त रूप से नहीं समझा सकी कि concurrency का उपयोग कहाँ करना चाहिए
- इसका मुख्य उपयोग
net/httpजैसी core libraries में server software को सरल बनाना था - यह हर program के हर हिस्से में इस्तेमाल होने वाली सुविधा नहीं थी
- इसका मुख्य उपयोग
- parallelism और concurrency के अंतर को देर से समझाना भी समस्या बना
- कई developers ने goroutine से code को parallel बनाकर तेज करने की कोशिश की, लेकिन वह उल्टा धीमा हो गया और वे हैरान रह गए
- concurrent code तभी parallelization से तेज हो सकता है जब समस्या स्वयं parallel हो, जैसे HTTP requests को संभालना
- इसकी भरपाई के लिए 2012 के Waza में “Concurrency is not Parallelism” प्रस्तुति दी गई
- यह स्पष्टीकरण और पहले आ जाना चाहिए था
- फिर भी, server software को संरचित करने के तरीके के रूप में concurrency को लोकप्रिय बनाने में Go का योगदान महत्वपूर्ण रहा
interfaces और composition-केंद्रित design
- interfaces, concurrency के साथ Go को अलग पहचान देने वाले प्रमुख विचारों में हैं
- Go interfaces, व्यवहार-केंद्रित object-oriented design के मूल विचार पर Go का उत्तर माने जा सकते हैं
- किसी type को कौन-सा interface implement करता है, यह पहले से घोषित करने की आवश्यकता न रखने वाला dynamic तरीका शुरुआत में आलोचना का विषय था, लेकिन Go शैली की programming के लिए महत्वपूर्ण बना
- standard library, testing और dependency management, interfaces की खुली प्रकृति पर काफी निर्भर हैं
- Go का
sort, interfaces पर काम करने वाले functions के रूप में implement किया गया है- यह कई लोगों के परिचित object-oriented style से अलग था, लेकिन एक शक्तिशाली विचार था
Reader,Writer, empty interface जैसे छोटे interfaces, Go libraries की नींव बने- ये प्रसिद्ध तीन interfaces औसतन 2/3 methods रखते हैं
- methods के छोटे sets, Go की विशिष्ट idiom बन गए
generics के इर्द-गिर्द लंबा घुमावदार रास्ता
- Go team ने देखा था कि generic programming अक्सर algorithm से पहले types के बारे में सोचने की प्रवृत्ति पैदा करती है
- वह शुरुआती abstraction की बजाय organic design को प्राथमिकता देती थी
- containers से अधिक functions को महत्व दिया गया
- Go ने map, slice, array और channel जैसे generic containers भाषा में शामिल किए, लेकिन उनकी genericity सीधे users को उपलब्ध नहीं कराई
- यह माना गया कि अधिकांश साधारण programming tasks के लिए ये types पर्याप्त होंगे, लेकिन हर मामले में ऐसा नहीं था, और यह सीमा कुछ users को असुविधाजनक लगी
- क्योंकि interfaces, Go programming की नींव थे, इसलिए नया polymorphism model interfaces के साथ अच्छी तरह मेल खाना ज़रूरी था
- कई प्रयास हुए और कुछ implementations बीच में छोड़ी गईं
- लंबी चर्चा के बाद Phil Wadler के नेतृत्व वाले type theorists की मदद भी ली गई
- अंततः Go ने interfaces को “method set” से “type set” में सामान्यीकृत करने के तरीके से generics को डिज़ाइन किया
- “generics” शब्द data structure-केंद्रित polymorphism से आया है, इसलिए यह Go के parametric polymorphism को पूरी तरह सटीक रूप से नहीं बताता, फिर भी व्यवहार में “generics” शब्द ही इस्तेमाल होने लगा
compiler: पहले C में, बाद में Go में
- शुरुआती Go compiler के C में लिखे जाने से language community के कुछ लोग असहज थे
- कुछ का मत था कि LLVM जैसे toolkit का उपयोग होना चाहिए था या Go को खुद self-hosting होना चाहिए था
- शुरुआती compiler को C में लिखने का कारण व्यावहारिक था
- नई language को bootstrap करने के लिए किसी मौजूदा language की ज़रूरत थी
- Ken Thompson पहले ही C compiler लिख चुके थे, और उसकी internal structure Go compiler की नींव बन सकती थी
- अगर language design करते समय उसी language में compiler भी लिखा जाए, तो design compiler लिखने के अनुकूल पक्ष में झुक सकता है
- शुरुआती compiler का generated code साधारण था और internals भी बहुत साफ नहीं थे, लेकिन वह छोटा, परिचित और जल्दी बदलने में आसान था
- अपने-आप बढ़ने वाली segmented stack जोड़ना इस निर्णय के लाभ का उदाहरण था
- यदि LLVM जैसे toolkit का उपयोग किया गया होता, तो ABI और garbage collector support में बदलाव सहित integration अधिक कठिन होता
- मूल Plan 9 compiler family से आई इसकी संरचना cross compilation के लिए भी उपयुक्त थी
- Go 1.5 में Russ Cox ने compiler को C से Go में semi-automatic translation करने वाला tool लिखा
- तब तक language परिपक्व हो चुकी थी, इसलिए compiler-केंद्रित language design की चिंता नहीं रही
- Go में आने के बाद testing, tooling, automatic rewriting और performance analysis जैसे Go के लाभ compiler development पर भी लागू किए जा सके
- आज LLVM-based Go compiler और कई अन्य compilers भी मौजूद हैं
open source project संचालन और community
- शुरुआत से ही यह माना गया कि सफल होने के लिए Go का open source project होना ज़रूरी है
- लेकिन core ideas और implementation को व्यवस्थित करने तक इसे private रखना अधिक productive समझा गया
- सार्वजनिक होने से पहले के पहले दो वर्ष लक्ष्य को स्पष्ट करने के लिए महत्वपूर्ण थे
- open source में बदलने के बाद community input बहुत अधिक आया, और खासकर Ian Taylor ने सवालों के जवाब देने में काफी समय लगाया
- Windows port पूरी तरह community ने Alex Brainman के मार्गदर्शन में किया
- Go team को community के साथ सहयोग का सर्वोत्तम तरीका समझने में लंबा समय लगा
- उन्हें लगता था कि वे अच्छी तरह संवाद कर रहे हैं, लेकिन गलतफहमियों और अपेक्षाओं के mismatch में समय नष्ट हुआ
- Go project ने कोड को जल्दी merge करके बाद में साफ करने की बजाय, merge से पहले ही उच्च code quality सुनिश्चित करने का तरीका चुना
- अनिवार्य code review
- details पर गहरी सावधानी
- यह तरीका community पर अधिक बोझ डालता है, इसलिए यदि contributors इसकी अहमियत न समझें तो उन्हें लग सकता है कि उनका स्वागत नहीं है
- Go project का repository history SVN, Perforce, Mercurial और Git से गुज़रा, लेकिन Russ Cox ने history को संरक्षित रखा, इसलिए मौजूदा Git repository में शुरुआती SVN changes भी मौजूद हैं
- Google, Go को support करता है, लेकिन Go team का agenda तय नहीं करता
- Google के अंदर का बड़ा Go codebase releases को test और validate करने के लिए इस्तेमाल होता है
- flow public repository से Google की ओर है, उल्टा नहीं
- core Go team को Google वेतन देता है, लेकिन वह स्वतंत्र रूप से काम करती है
package management में trial and error
- Go language की package design स्वयं उत्कृष्ट थी, लेकिन package management के विकास की प्रक्रिया अच्छी नहीं रही
importstatements में paths को साधारण strings के रूप में देना एक महत्वपूर्ण और flexible निर्णय था- समस्या यह थी that सिर्फ standard library वाले चरण से web से code लाने वाले model तक का संक्रमण कठिन रहा
- शुरुआती Go team, Google के monorepo और head से build करने वाले तरीके की आदी थी
- कई versions वाले packages के package managers का अनुभव पर्याप्त नहीं था
- dependency graph को resolve करने की technical complexity को शुरुआत से ठीक तरह नहीं संभाला गया
- deps.dev पर किया गया काम इस समस्या के लिए एक तरह के compensation की तरह देखा जाता है
- community के साथ dependency management की समस्या हल करने की कोशिश सद्भावना से हुई, लेकिन जब अंतिम design सामने आया तो community के कई लोग स्वयं को अलग-थलग महसूस करने लगे
- यह अनुभव इस बात का सबक बना कि community participation वास्तव में कैसे होनी चाहिए, और बाद में इसमें काफी सुधार हुआ
- अब design स्थिर है, तकनीकी रूप से उत्कृष्ट है और अधिकांश users के लिए अच्छी तरह काम करती दिखती है
documentation और executable examples
- शुरुआती documentation काफी लिखी गई थी, लेकिन वह community की अपेक्षाओं से अलग थी
- सबसे बड़ी कमी यह थी कि सबसे साधारण function के लिए भी usage examples चाहिए होते हैं
- यह बताने से अधिक मूल्यवान था कि वह क्या करता है, यह दिखाना कि उसे कैसे इस्तेमाल किया जाता है
- अब documentation में बहुत से examples हैं, जिनमें से अधिकांश open source contributors ने दिए हैं
- Go ने शुरुआती दौर में examples को web पर executable बनाया
- 2012 के Google I/O presentation में concurrency दिखाने वाले code snippets को browser में चलाया जा सकता था
- Andrew Gerrand ने इसे संभव बनाने वाली web technology लिखी
- बाद में यह blogs और online package documentation तक फैल गई
- The Go playground लोगों के लिए code आज़माने और विकसित करने के सार्वजनिक sandbox के रूप में उपलब्ध है
14 साल बाद Go की स्थिति
- language design और development process में लिए गए निर्णयों की वजह से Go सिर्फ एक programming language नहीं, बल्कि software लिखने का एक तरीका बन गया
- Go की वर्तमान स्थिति तक पहुँचने में इन तत्वों का योगदान रहा
- server code के लिए ज़रूरी अधिकांश बुनियादी सुविधाएँ देने वाली मजबूत standard library
- language का first-class हिस्सा होने वाली concurrency
- inheritance की बजाय composition पर आधारित approach
- dependency management को स्पष्ट करने वाला packaging model
- integrated और तेज build तथा test tools
- सख्त और consistent formatting
- चतुराई से अधिक readability को महत्व देने वाला दृष्टिकोण
- compatibility guarantee
- Go community विविध और सक्रिय Gophers से बनी है, और project की सफलता में उसकी केंद्रीय भूमिका है
- नतीजतन, Go code किसी ने भी लिखा हो, वह काफ़ी हद तक समान दिखता और काम करता है; language के अलग-अलग subsets इस्तेमाल करने वाले गुट अपेक्षाकृत कम हैं; और समय बीतने पर भी code compile और run होता रहे, इसकी गारंटी दी जाती है
- ऐसी विशेषताएँ प्रमुख programming languages में दुर्लभ परिणाम मानी जा सकती हैं
1 टिप्पणियां
Hacker News की टिप्पणियाँ
यह सचमुच सराहनीय है कि मुख्य लोग समय निकालकर retrospective लिखते हैं, और यह उन लोगों के लिए बहुत मददगार है जो अभी सच में बदलाव लाना चाहते हैं
हालांकि मुझे लगता है कि Rob Pike ने Go ने जो सही किया, उसे पर्याप्त स्पष्टता से नहीं बताया। Go ने सिर्फ भाषा को ही नहीं, बल्कि project पर काम करने वाली शक्तियों को भी अच्छी तरह मैनेज किया: लक्ष्य उपयोग को system programming तक सीमित रखा, भाषा और सिद्धांतों को स्पष्ट रूप से परिभाषित किया, release से पहले समस्याएँ ठीक करने वाला quality-first रवैया बनाए रखा, और भाषा, release और core messaging को कड़ाई से नियंत्रित करते हुए भी निचले क्षेत्रों में community को नेतृत्व करने की गुंजाइश दी
एक कम आँकी गई बात यह है कि Google ने खुद बहुत ज्यादा दखल नहीं दिया। शायद इसलिए कि Go वास्तव में Google के लक्ष्यों में योगदान देता था और महत्वपूर्ण था। आज नए projects में ऐसा हो पाएगा या नहीं, यह सवाल है
Dart को Flutter के बाहर लगभग अपनाया नहीं गया, जो इस वास्तविकता की तुलना में दिलचस्प है कि application code system code से कहीं ज्यादा होता है। Go वह core technology था जिसने server-side software को Java के भारी-भरकम environment से native containers में शिफ्ट किया, और पिछले 10 साल की web application infrastructure के बड़े हिस्से की नींव बना। उस growth के दौरान भी team छोटी रही और key लोग भी बने रहे, लेकिन आगे भी ऐसा रहेगा या नहीं, पता नहीं
इसलिए design के लिहाज से यह system programming language के रूप में उपयुक्त नहीं है। शुरुआती दिनों में उसने खुद को ऐसा कहा था, जिससे एक अजीब मिथक लंबे समय तक बना रहा, लेकिन लगता है वह wording कुछ साल पहले हटा दी गई
Go, C या Rust का competitor होने से ज्यादा Java family का competitor है, और उसका वास्तविक use area देखने पर यह स्पष्ट होता है। हालांकि सीमित type system और बेहतरीन concurrency support के कारण वह stack में अक्सर थोड़ा नीचे की layer में जगह बना लेता है
खासकर version management में ऐसा था। शुरू में Go ने समस्या को मानने से ही इनकार किया, फिर जब community consensus बढ़ा तो पुरानी चर्चाओं को भूलकर तय कर लिया कि अब modules पर जाएंगे
यह भी याद है कि Cloudflare को daylight saving time outage झेलने से पहले तक वे monotonic time को public API बनाना नहीं चाहते थे
Java world में नई features, जैसे बढ़िया streams, का इस्तेमाल करके codebase refactor करने का मज़ा होता है। Go में मज़ा सबसे सरल और साधारण abstraction खोजने से आता है
हर भाषा को किसी खास domain के लिए libraries और frameworks चाहिए होते हैं। Flutter, Dart में client apps लिखने का framework है, और अगर Flutter न हो तो Dart language आपको कितनी भी पसंद हो, Android और iOS पर screen पर pixels draw करने के तरीके से ही शुरुआत करते हुए बहुत समय लगाना पड़ेगा
application लिखने वाला हर व्यक्ति libraries और framework stack के ऊपर build करता है। Go और Flutter से जुड़े Dart में फर्क बस इतना है कि server के लिए जरूरी networking, serialization, cryptography जैसी domain libraries Go में standard library में शामिल हैं
Dart में collections और async जैसे basic elements built-in libraries के रूप में हैं, लेकिन domain-specific capabilities के लिए वह Flutter जैसे external packages पर निर्भर करता है। इसकी बड़ी वजह यह है कि Dart के पास बहुत शुरुआती दौर से ही मजबूत package management तरीका था। Dart team द्वारा लिखी और maintain की जाने वाली कई core libraries भी built-in के बजाय package manager से distribute होती हैं, क्योंकि उन्हें evolve करना इस तरह कहीं आसान है
UI frameworks की उम्र language से कम होती है, इसलिए मेरे हिसाब से Flutter का Dart standard library में चिपका न होना बेहतर है। Flutter शानदार है, लेकिन 20 साल बाद उससे बेहतर कुछ आ जाए तो हैरानी नहीं होगी, और तब अगर Flutter built-in नहीं होगा तो Dart users ज्यादा आसानी से switch कर पाएंगे
Android जैसे fake FOSS को strategic रूप से tolerate करते हैं, लेकिन उसके अलावा अगर वह ad clicks में नहीं बदलता तो उसे बेकार समझने का माहौल है
2012 में Python developer के रूप में मुझे Modbus के लिए bit manipulation करनी थी, इसलिए मैंने Go उठाया। वह code मैंने deploy नहीं किया, लेकिन bits और bytes से काम करना इतनी आसानी से बस चल गया कि मैं चौंक गया
10 साल बाद, और लगभग full-time Go के कई काम करने के बाद भी, यह बात अब भी हैरान करती है कि ज्यादातर चीजें बस चल जाती हैं
मुझे Go language और Go community पसंद है। Rob, Ian, Russ आदि जो लोग Go बनाते हैं, उनके काम के लिए आभारी हूँ, और इस presentation/article ने community के साथ “rough patches” को ईमानदारी से address किया, इसे भी मैं सराहता हूँ
हालांकि उस दौर से गुजर चुके व्यक्ति के तौर पर, खासकर package management की अव्यवस्था में, यह मानना बहुत मुश्किल है कि उन्हें ठीक-ठीक पता नहीं था कि वे कैसे व्यवहार कर रहे हैं। फिर भी article का यह आकलन सही है कि अब वे अच्छे solution तक पहुँच गए हैं
उम्मीद है कि अगले 10 सालों में Go, और Zig·Deno जैसी उससे प्रेरित मिलती-जुलती languages, healthy communities के रूप में आगे बढ़ती रहेंगी
मैं programmer से ज्यादा computational biologist हूँ, इसलिए Go का इस्तेमाल असमान रहा है, लेकिन जब फिर Go पर लौटता हूँ तो पुराना code compile हो जाता है और भाषा वैसा ही व्यवहार करती है जैसा उम्मीद थी
साथ ही, मुझे अच्छा लगा कि Rob Pike ने चमकदार features को सब नहीं अपनाया, फिर भी community participation सीखने की प्रक्रिया में हुई गलतियों को स्वीकार किया
कुछ तुरंत चल गए, कुछ में AI model की API information थोड़ी पुरानी थी, इसलिए थोड़ा सुधार करना पड़ा
फिर भी program को server पर
scpसे copy करने पर वह बस चल जाता है—यह Python में हर बार “इस बार का trendy venv system क्या है” वाला dance करने की तुलना में कमाल हैशानदार लेख है, लेकिन काफी आत्म-प्रशंसात्मक भी लगता है। मुझे लगा था कि भाषा की गहरी समस्याओं को स्वीकार किया जाएगा, पर शायद मूल बात यह है कि उनके हिसाब से भाषा परफेक्ट है और समस्या मैं हूं
इसलिए Go की सिफारिश करना मुश्किल है। type system, error handling, असुरक्षित concurrency, जरूरत से ज्यादा सरल syntax,
nil, default zero values, और कई उपेक्षित mainstream packages जैसी समस्याएं हैंफिलहाल मैं Rust को अपनी मुख्य भाषा के रूप में इस्तेमाल करता हूं। ecosystem सक्रिय है और कई मामलों में उसमें वह vision है जो Go में नहीं है
और तीखे ढंग से कहूं तो, जब Go backend language के रूप में PHP से मुकाबला कर रहा था, तब शायद उसका समय था
पहला paragraph “मुझे पसंद नहीं”, दूसरा sarcasm, तीसरा भी “मुझे पसंद नहीं”, चौथा “Rust बेहतर है”, और पांचवां फिर sarcasm जैसा दिखता है
deps.devहटाने के बाद छोड़ दियाकुछ साल बाद कंपनी में काम पूरा कराने के लिए टीम में Go लाने की कोशिश की, पर एक दूसरे engineer ने समय लगाकर language देखी और बताया कि यह क्यों अच्छी नहीं है, और वह सही था
मुख्य बात यह थी कि यह सरल तो है, लेकिन error handling और unused imports जैसी छोटी-छोटी चीजें इसे इस्तेमाल करना पीड़ादायक बना देती हैं। व्यक्तिगत रूप से मुझे error handling पसंद थी, लेकिन type system नापसंद था
पहले OCaml इस्तेमाल किया था, इसलिए try/catch या
nilकी तुलना मेंOptionऔरResulttypes की value जानता था, लेकिन आसान होने के कारण Go इस्तेमाल करता था। मगर उस आसानी के साथ long-term maintenance cost आती है। बेहतर है कि शुरुआत से सही किया जाए और बाद में मुश्किलों का सामना न करना पड़ेहल्के में कहने की कोशिश नहीं है, लेकिन Go को अक्सर ऐसी भाषा बताया गया है जिसने पिछले 50 सालों की programming language progress उठाकर फेंक दी
यह बुरे अर्थ में नहीं है। Rob Pike को concurrency के असुरक्षित होने या
nilके billion-dollar mistake होने की चिंता नहीं है, और ज्यादातर Go users को भी नहींक्या इससे Go अच्छी भाषा बन जाती है? नहीं। लेकिन मुद्दा वह नहीं है। Go एक सुविधाजनक और पर्याप्त रूप से ठीक-ठाक भाषा है, जिसे इस्तेमाल में आसान बनाने वाले compelling tools के साथ जोड़ा गया है। Programming languages की दुनिया का Crocs जैसा अस्तित्व है
अगर high integrity और safety चाहिए तो मैं Ada या formal verification के साथ Ada/SPARK इस्तेमाल करूंगा। बाकी मामलों में Go, Rust की तुलना में कहीं ज्यादा productivity देता है
व्यक्तिगत रूप से Rust मुझे C++ की तरह overengineering का प्रमुख उदाहरण लगता है
कड़वा लगेगा, लेकिन 10 साल पहले
go-nutsपर मेरा मजाक उड़ाया गया था और Rob Pike से भी dismissive जवाब मिला था। वजह यह थी कि मैंने यह कहने की हिम्मत की थी किgo getऔर network के जरिए module import करने का तरीका जिस तरह काम करता है, beginner docs में हर जगह promote किया जाता है, और पूरी community में उसी तरह इस्तेमाल होता है, वह आखिरकार harmful और short-sighted हैअजीब global namespace tree उसके अंदर तो समझ आता है, और checked-in generated code पर जोर देना भी यह देखकर समझ आता है कि Google monorepo में build artifacts तक शामिल करता है
हां,
v2problem या हमेशा काv0problem है। फिर भी npm इस्तेमाल करने या Python की किसी ऐसी चीज से निपटने से बेहतर है, जिसे देखकर मन में भगवान को याद करना पड़ेयह लेख ACM में छपे लेख [1] का ज्यादा personal version लगता है। दोनों लेख मानते हैं कि उन्होंने language specification के नजरिए से कोई शानदार नई programming language नहीं बनाई, बल्कि programming language के आसपास की चीजों को बहुत अच्छे से बनाया, और वह शायद ज्यादा महत्वपूर्ण हो सकता है
जमा किए गए लेख में कहा गया है कि उन्होंने interface usage approach और concurrency approach का आविष्कार किया। लेकिन goroutine, Haskell thread जैसा ही है, और interfaces, अगर वे आज की तरह generic arguments support करें, तो Haskell typeclass से बहुत मिलते-जुलते हैं। यह देखते हुए कि Haskell, Go से पहले था, यह देखना दिलचस्प है कि procedural programmers स्वतंत्र रूप से functional programming ideas की ताकत खोजते हैं
Go की एक language innovation यह है कि interface implementation को यह declare करने की जरूरत नहीं होती कि वह कौन-सा interface implement करता है। safety के नजरिए से यह भयानक है, लेकिन व्यवहार में शायद ही समस्या बनता है, और Haskell और Rust में आने वाली भयानक cyclic dependency problems को हटा देता है
[1] https://cacm.acm.org/magazines/2022/5/260357-the-go-programm...
अगर types को पहले से declare करना पड़ता कि वे कौन-सा interface implement करते हैं, तो Go पूरी तरह अलग भाषा होती। यह Go के मुख्य differentiators में से एक था। कम से कम तब तक, जब तक TypeScript अलग वजहों से structural typed interfaces लेकर नहीं आया
क्या हम ऐसी कोई programming language बता सकते हैं जिसकी हमें परवाह हो, जो interface define करने पर उस interface implementation को अनिवार्य रूप से provide करने के लिए मजबूर करती हो?
Go की क्रिप्टोग्राफी से जुड़ी समस्याओं का अभी ज़्यादा ज़िक्र नहीं हुआ लगता है। OpenSSH द्वारा SHA1 को हटाने के बाद Go टीम को
x/crypto/sshमें SHA2 सपोर्ट जोड़ने में पूरा 1 साल लग गया [1]। Gitea इसका एक प्रसिद्ध पीड़ित था [2]इसके अलावा, क्रिप्टोग्राफी maintainer का GnuPG पर हमला करना [3], और Dan Bernstein की विश्वसनीयता कमज़ोर करने की कोशिश करते दिखना [4] भरोसा नहीं जगाता
[1]: https://github.com/golang/go/issues/49269
[2]: https://github.com/go-gitea/gitea/issues/17798
[3]: https://twitter.com/FiloSottile/status/1127643698676797441
[4]: https://twitter.com/FiloSottile/status/1555669786826244096
x/packages अक्सर काफी उपेक्षित रहते हैं, और ऐसा development model अपनाते हैं जो नए contributors के लिए patches को सफलतापूर्वक भेजना मुश्किल बना देता हैइसके उलट, standard library का crypto package मुझे Go की बड़ी सफलताओं में से एक लगता है। OpenSSL लंबे समय तक एक आपदा रहा, और Go ने, खासकर agl ने, एक बहुत व्यापक रूप से इस्तेमाल होने वाला वैकल्पिक implementation बनाया और deploy किया, जिसकी औसत quality भी ऊंची थी। खासकर public API में foot-guns बहुत कम होने के लिहाज़ से
यह लेख
gofmtको “सही किया गया काम” बताता है, और यह खास तौर पर रेखांकित करने लायक हैकई language designers और advocates को यह मामूली लग सकता है, लेकिन Go project के पहले दिन से ही एक आम bikeshedding बहस को पूरी तरह खत्म कर देने के कारण यह बहुत बड़ा मूल्य देता है
मैंने देखा है कि कई नई languages ने इसे जानबूझकर कॉपी किया, या यह सोचकर अपनाया कि Go में है तो हमें भी बनाना चाहिए
मैंने काफी अजीब formatting rules भी देखे हैं, लेकिन ऐसे rules बहुत कम थे जिनकी आदत न पड़ सके। इसके उलट, जिन codebases में अनिवार्य formatting rules नहीं थे, उनमें नतीजतन code समझना अक्सर ज़्यादा मुश्किल लगा
go fmtउसे ज़्यादातर बेकार बना देता है“21वीं सदी का server code लिखने के लिए ज़रूरी ज़्यादातर चीज़ों वाली एक ठोस और अच्छी तरह बनाई गई library का होना बड़ी संपत्ति था” — इस बात से सहमत हूँ
Go को Google ने इसलिए fund किया क्योंकि Google के पास एक समस्या थी। उन्हें server-side application code बहुत लिखना था, और Python बहुत धीमा था जबकि C++ बहुत fragile था। Go उस खाली जगह में बहुत अच्छी तरह fit हुआ
बड़ा bonus यह था कि Google के लोगों ने ऐसे काम के लिए ज़रूरी libraries लिखीं और उन्हें internally भी इस्तेमाल किया। इसलिए जब आप library इस्तेमाल करते थे, तो error paths तक भी काफी हद तक ऐसा code होता था जो असल में execute हो चुका था
भाषा को लेकर तकनीकी असहमतियाँ हैं। खासकर शुरुआत में हर चीज़ के लिए queue इस्तेमाल करने पर ज़ोर, यहाँ तक कि queue से mutex बनाने वाला हिस्सा भी। लेकिन use case के हिसाब से green thread उन्होंने सही पकड़ा। function color problem सचमुच की समस्या है, और Rust में async और threads को साथ इस्तेमाल कराने के लिए जो कठिन tricks चाहिए, वह बहुत ज़्यादा है। Go थोड़ी performance छोड़कर बड़ी simplicity हासिल करता है
पुराने समय की thread-नापसंदगी भी दिलचस्प है। मैंने UNIVAC mainframe से शुरुआत की थी; 1967 से ही threads थे और तब उन्हें “activities” कहा जाता था। 1972 में न सिर्फ user-space threads थे, बल्कि वे symmetric multiprocessor पर चलते थे। asynchronous I/O और user-space completion functions, locks के लिए built-in instructions, और internally threaded operating system भी था
threads और multiprocessors को सामान्य माना जाता था, और UNIX पर जाते हुए लगा कि ये चीज़ें खो गईं। UNIX/Linux को इस क्षेत्र में catch up करने में दशकों लगे, और programmers की कई पीढ़ियाँ shared-memory multiprocessor इस्तेमाल करना नहीं जानती थीं
Dijkstra की शुरुआती concurrency theory को अलग शब्दों और अक्सर खराब designs के साथ फिर से invent किया गया। Go के लोगों ने Dijkstra के bounded buffer और length 0 व 1 वाले bounded buffer क्यों उपयोगी हैं, यह समझा। उसे फिर से देखना अच्छा था। सही primitive operations हों तो concurrency इतनी मुश्किल नहीं होती। अगर classic pthreads-style सोच की तरह यहाँ-वहाँ locks छिड़ककर हल करने की कोशिश करें, तो यह ठीक से काम नहीं करता। UNIX/Linux के शुरुआती दौर में CPU scheduler सच में बहुत खराब था, इसलिए thread unblocking भयानक ढंग से काम करता था—इससे भी मदद नहीं मिली
Go इसलिए बना क्योंकि Rob Pike को समस्या थी। उन्हें C++, Java, Python पसंद नहीं थे, और क्योंकि वे Rob Pike थे, Google ने उन्हें कुछ वर्षों तक नई भाषा पर काम करने की अनुमति दी। ऐसा नहीं था कि Larry, Sergey, Sundar या Eric Rob के पास जाकर उनसे कुछ करने को कहने गए हों
कुल मिलाकर Go को funding इसलिए मिली क्योंकि Rob Pike ने support माँगा, Google Rob Pike को अपने साथ बनाए रखना चाहता था, और उसे लगा कि वे लगभग निश्चित रूप से कुछ दिलचस्प करेंगे। Google Engineering में company का पैसा वस्तुतः लोगों को काम करने की अनुमति देने जैसा ही होता है
Rob Pike एक बेहतरीन engineer थे, इसलिए Go कुछ पहलुओं में काफी अच्छा निकला, और Google operational applications लिखने के लिए C++, Python, Java इस्तेमाल करने की उनकी शुरुआती समस्या से अच्छी तरह मेल खाता था। Google के अंदर कुछ लोग भी सहमत हुए और Go इस्तेमाल करना शुरू किया, और उनमें से कुछ प्रभावशाली लोगों ने दूसरों को Python की जगह Go इस्तेमाल करने के लिए “encourage” किया
2009 में Google में Python, YouTube acquisition के मामले को छोड़कर, कुछ internal utilities के बाहर servers में शायद ही इस्तेमाल होता था, इसलिए उस विचार को तुरंत छोड़ सकते हैं। Google ने खुद जो सबसे बड़ा Python server बनाया था, वह Guido van Rossum द्वारा लिखा code review tool Mondrian था, जिसे बाद में Java में लिखे Critique ने replace कर दिया
उस समय Google के पास programming language wars खत्म करने के लिए काफी सख्त three-language policy थी। C++, Java, Python इस्तेमाल किए जा सकते थे; Python scripts के लिए, C++ infrastructure के लिए, और Java web servers के लिए इस्तेमाल होता था
overlap भी था। Java C++ से देर से introduce हुआ था, और कुछ web servers को फिर से लिखना कठिन था, इसलिए वे C++ में ही रहे। Java में gradual porting की कोशिशें भी हुईं, लेकिन वे बहुत ज्यादा जम नहीं पाईं। Megastore जैसे कुछ infrastructure servers Java में लिखे गए थे, और core libraries आम तौर पर C++ में थीं, जिनके साथ JNI और Python bindings लगे होते थे
policy में gaps भी थे, इसलिए कुछ teams ने internal infrastructure के लिए अपनी languages बनाईं, और असली codebase में custom configuration languages और Rob Pike द्वारा बनाया गया Sawzall भी इस्तेमाल होता था। syntax Go से थोड़ा मिलता-जुलता था
कुल मिलाकर लोगों के पास choice की काफी आज़ादी थी, और नए web servers आमतौर पर Java में, जबकि नए database engine जैसी चीज़ें आमतौर पर C++ में लिखी जाती थीं
asynchronous code लिखना निश्चित रूप से कठिन था, और Go ने उस हिस्से को सही तरीके से हल किया—यह बात सही है। लेकिन Go Google की समस्या हल करने के लिए senior management द्वारा शुरू किया गया project नहीं था। ऐसा नहीं कि management ऐसे projects से डरता था; बल्कि developer pain points सीधे हल करने के लिए संबंधित teams द्वारा बनाए और staffed किए गए internal infrastructure projects बहुत बड़ी संख्या में थे। उस समय आगे बढ़ाए गए बड़े networking upgrades जैसी चीज़ें इसके उदाहरण हैं। ऐसे projects पहले से announce किए जाते थे
मुझे याद नहीं कि उस स्थिति को लेकर कोई बड़ा असंतोष था। build time Bazel/Blaze के remote build cluster के साथ काम करने से पहले तक समस्या था, और उसके बाद सब कुछ remote-cache हो जाता था, इसलिए बहुत बड़े codebase भी कुछ seconds में compile हो सकते थे। local compiler speed लगभग महत्वहीन हो गई, और
javacभी बहुत तेज़ थाGo की घोषणा काफी surprising थी। अगर इसे सच में Google की समस्या हल करने के लिए develop किया गया होता, तो सामान्य Google तरीका यह होता कि पहले इसे internally release किया जाता, internal users के साथ कुछ वर्षों तक evolve किया जाता, फिर public किया जाता। लेकिन Go पहले public हुआ। आसपास के लोग सोच रहे थे कि यह किस लिए है, और अगर यह सच में internal demand से driven होता तो ऐसी प्रतिक्रिया की उम्मीद करना मुश्किल था
Ken Thompson के C compiler को LLVM के बजाय इस्तेमाल करने के फैसले वाला हिस्सा दिलचस्प है। लोगों ने शिकायत की थी और खासकर शुरुआती versions में इससे generated code कम optimized होने का नतीजा भी निकला था
इसके बदले split stacks जल्दी बना पाना संभव हुआ। अगर इसे LLVM में implement करना और LLVM ABI के हिसाब से ढालना पड़ता, तो शायद यह बिल्कुल भी नहीं हो पाता
लेख में इसे इस फैसले का अकेला फायदा नहीं, बल्कि फायदे का एक उदाहरण बताया गया है
X86FrameLowering में पहले से support था, और Go के release के समय भी मौजूद था। stack splitting चालू करने पर LLVM function prologue में checks emit करता है, और जरूरत पड़ने पर
__morestackcall करके और stack allocate करता है। Windows MSVC ABI को भी_chkstksupport के लिए बहुत मिलता-जुलता code चाहिए, इसलिए यह__morestacksupport के साथ स्वाभाविक रूप से फिट बैठता हैउलटे, GDB को split stacks समझाना compiler implementation के किसी भी हिस्से से ज्यादा मुश्किल था। यह backend से स्वतंत्र है
शायद लेखक ने relocatable stack के साथ भ्रम किया है। उस समय precise garbage collection की जरूरत थी, इसलिए इसे implement करना कठिन था, लेकिन अब Azul ने इसे LLVM में implement कर दिया है। उस समय precise garbage collection implement करने का सबसे आसान तरीका function calls के आर-पार सभी registers को spill करना था, जिसमें implementation effort ज्यादा लगता, लेकिन वह अत्यधिक नहीं था। Plan 9 compiler वैसे भी ऐसा करता दिखता है, इसलिए 6g/8g के मुकाबले performance regression भी नहीं होता। खैर, Azul के garbage collection support में अब roots को registers में store कर सकने वाला ठीक-ठाक implementation मौजूद है
इसलिए experiment कर पाने की क्षमता valuable थी, यह सही है, लेकिन यह खास example कोई perfect case नहीं है
[1]: https://go.dev/doc/go1.3#:~:text=Go%201.3%20has%20changed%20...
static typed hobby language की कल्पना करते हुए मैं interfaces और generics को साथ में संभालने वाली कोई चीज आजमा रहा हूं, और खुद महसूस किया कि Rob सही कहते हैं। दोनों को अच्छी तरह साथ फिट कराना सचमुच मुश्किल है
फिर भी मुझे लगता है कि यह करने लायक है। निजी तौर पर, ऐसी statically typed language इस्तेमाल करना बहुत rewarding नहीं लगेगा जिसमें users अपने generic types define नहीं कर सकते
पहले मैंने BASIC में ऐसे environment में programming की थी जहां subroutines के लिए
GOSUBतो था, लेकिन arguments pass करने वाली subroutines लिखने का तरीका नहीं था। मैं उस अनुभव को type system के level पर दोहराना नहीं चाहताइसलिए समझ आता है कि Go team को अच्छा design खोजने में इतना समय क्यों लगा। अच्छी language design करना मुश्किल है, type system वाली अच्छी language design करना 10 गुना मुश्किल है, और generics वाले type system को design करना उससे भी 10 गुना मुश्किल है
यानी Go ने generics implementation का सबसे आसान हिस्सा उठाया। इसे न करने की वजह technical नहीं थी। यह ideological थी, पूरी तरह अज्ञानता पर आधारित थी, और इसलिए थी कि Go designers में से ज्यादातर ने 90s के अंत के बाद programming language theory के field को follow करना बंद कर दिया था