- 20 साल से अधिक समय तक software लिखने के अनुभव से, मजबूत static typing REPL या one-off scripts जैसे अपवादों को छोड़कर लगभग हमेशा चुनने लायक होती है
- Types caller और callee के बीच के contract को code में छोड़ते हैं, जिससे गलत parameters या return values को compile/type-check के समय ही पकड़ा जा सकता है
- HTML input से आई string
"20"को number की तरह इस्तेमाल करने पर"201"बन जाने का उदाहरण runtime से पहले पकड़ी गई error और customer को दिखने वाली error के फर्क को दिखाता है - Svix, Redis keys, cache values,
PersonId·PetIdजैसे identifiers, और API input validation को type system में डालकर typos और गलत ID पास होने की समस्या घटाना चाहता है - Types छोड़ देने से शुरुआती implementation तेज हो सकती है, लेकिन documentation·testing·debugging की लागत बढ़ती है; type inference और IDE support इस्तेमाल करने से refactoring और onboarding आसान हो जाते हैं
Static types पर जोर देने की वजह
- मजबूत static typing एक अच्छे विचार से आगे बढ़कर, ज्यादातर software के लिए सही default के करीब है
- Type-less languages या variants भी उपयोगी हैं
- REPL का इस्तेमाल
- ऐसे माहौल में one-off scripts जहां पहले से types लगभग नहीं हैं, जैसे shell
- इनके अलावा ज्यादातर मामलों में मजबूत types पसंद हैं
- Types का इस्तेमाल न करने से अभी development speed बढ़ सकती है, लेकिन इसे “पूरी रफ्तार से खाई की तरफ जाने” जैसा मानता हूं
- आखिरकार विकल्प दो में से एक है
- ज्यादा काम करके invariants को compile या type-check के समय verify करना
- कम काम करके runtime पर verify करना, या runtime पर भी verify न करना
- Runtime errors development के दौरान हमेशा नहीं पकड़ी जातीं, और पकड़ी भी जाएं तो customer को दिखने वाले तरीके से हो सकती हैं
- Tests मदद करते हैं, लेकिन हर संभव गलत function parameter type को test करना मुश्किल है; गलत types को types से रोकना आसान माना जाता है
Types सीधे code contracts और bugs घटाने से जुड़े हैं
- Types इंसानों और tools दोनों के लिए उपयोगी code comments हैं, और code के टुकड़ों के बीच contract को ज्यादा सख्त बनाने वाला mechanism भी
- एक ही birthday greeting function में भी contract की स्पष्टता बहुत अलग हो सकती है
birthdayGreeting1(...params)में parameters की संख्या तक दिखाई नहीं देती, इसलिए documentation पढ़े बिना behavior समझना मुश्किल हैbirthdayGreeting2(name, age)से name और age होने का hint मिलता है, लेकिन type नहीं हैbirthdayGreeting3(name: string, age: number): stringinput और return type तक contract में शामिल करता है
- अगर function को
age + 1इस्तेमाल करने के लिए बदला जाए, तो type-less version में string input पर समस्या आती है- HTML input से आई value हमेशा string हो सकती है
birthdayGreeting2("John", "20")"John will turn 201 next year!"return करता है- Type वाला version गलत call को compile में fail कर देता है, क्योंकि
agenumber होना चाहिए
- Caller और callee के बीच का contract codebase बड़ा होने पर और महत्वपूर्ण हो जाता है
- Callee बदलने पर caller पर क्या असर होगा, यह पता चल सकता है
- खासकर open source libraries जैसी स्थिति में, जहां अलग-अलग लोग caller और callee लिखते हैं, यह बहुत जरूरी है
- ऐसा contract न हो तो changes का असर कहां तक जाएगा, यह समझना मुश्किल होता है
Developer experience, refactoring और onboarding में फायदे
- Type information को IDE और development tools इस्तेमाल करके developer experience को काफी बेहतर बनाते हैं
- Code लिखते समय expectation गलत होने पर तुरंत पता चल जाता है, जिससे cognitive load कम होता है
- Developer को मौजूदा context के सभी variables और functions के types याद रखने की जरूरत नहीं होती; compiler mismatch बता देता है
- Refactoring भी आसान हो जाती है
- Function implementation बदलते समय compiler बता सकता है कि कहीं और की assumptions टूट रही हैं या नहीं
- नए engineer के लिए codebase या library में adapt करना भी आसान होता है
- Type definitions को follow करके समझा जा सकता है कि वे कहां इस्तेमाल हो रही हैं
- बदलाव करने पर compile errors आती हैं, इसलिए experiment करना आसान होता है
Persontype लेने वाले function के उदाहरण में फर्क दिखता हैbirthdayGreeting3(person: Person)में IDE सेPersonके use points खोजना आसान है- Type-less
birthdayGreeting2(person)वास्तव मेंPersonexpect करता है, यह जानने के लिए पूरे codebase को पढ़ना पड़ सकता है
- Documentation कुछ हद तक मदद कर सकती है, लेकिन docs आसानी से outdated हो जाते हैं और types code में ही बची रहने वाली documentation बन जाते हैं
- Types को उपयोगी variable names के ज्यादा मजबूत रूप जैसा माना जाता है
Svix type system में information कैसे डालता है
- Svix संभव हो उतनी ज्यादा information type system में डालकर compile time पर पकड़ी जा सकने वाली errors घटाना और developer experience सुधारना चाहता है
- Redis मूल रूप से string-based protocol है और उसमें built-in types नहीं हैं, इसलिए Redis layer में types के फायदे खत्म हो सकते हैं
- साधारण cache example में दो bugs हैं
person-{id}औरpreson-{id}जैसे key name typos हैं- Person data को
Pettype के रूप में load करने की कोशिश है
- Svix ऐसी समस्याओं से बचने के लिए दो चीजें लागू करता है
- Key को सामान्य string नहीं, बल्कि specific type के रूप में require करना
- Key और value को मजबूती से pair करना
- उदाहरण के लिए
PersonCacheKey::new(id)से बनी key इस्तेमाल करने पर,cache.get(PersonCacheKey::new(id))के result कोPetके रूप में लेने वाला code compile में fail हो जाता है - साधारण
StringID भी mistakes को आमंत्रित करती हैdo_something(id: String)से स्पष्ट नहीं होता कि कौन-सी ID चाहिए- जहां
pet.idपास करना चाहिए, वहां गलती सेpet.ownerपास हो सकता है
- Svix हर ID के लिए अलग type रखता है
PersonId(String)PetId(String)PetकाownerPersonIdहोता है
- API से मिली ID की validity को भी type creation से जोड़ता है
- उदाहरण के लिए pet ID का format
pet_prefix के बाद Ksuid होना है PetIdको validation के बिना create नहीं किया जा सकता- इस तरीके से जब database में pet न मिलने पर
404 Not Foundreturn किया जाता है, तो भरोसा किया जा सकता है कि ID format खुद valid था - Invalid ID को API handler में पहले ही
422या400के रूप में handle कर लिया जाता है
- उदाहरण के लिए pet ID का format
विरोधी तर्क और tools की भूमिका
- Types के खिलाफ मुख्य arguments development speed, learning curve और type complexity, effort और boilerplate हैं
- Types के बिना prototyping निश्चित रूप से तेज हो सकती है
- Compiler की शिकायत के बिना code comment out किया जा सकता है
- सही value तय होने तक fields में गलत values डाली जा सकती हैं
- लेकिन इसे aggressive और unnecessary technical debt माना जाता है, जिसकी कीमत local, test suite और production में debugging करते समय कई बार चुकानी पड़ती है
- Learning curve मौजूद है, लेकिन ज्यादातर लोगों को type expert बनने की जरूरत नहीं होती
- Simple type expressions से भी पर्याप्त काम किया जा सकता है
- अटकने पर मदद ली जा सकती है
- Developers को पहले से coding, React, Axum जैसे frameworks आदि बहुत कुछ सीखना पड़ता है, इसलिए type learning का बोझ बढ़ा-चढ़ाकर बताया जाता है
- Type learning एक बार की लागत है, और किसी specific codebase में onboarding के समय types से मिलने वाला फायदा उससे बड़ा है
- Types का इस्तेमाल न करने पर basic safety पाने के लिए काफी documentation और testing चाहिए
- Docs और tests outdated हो सकते हैं
- सही types जोड़ना कम effort वाला माना जाता है
- जिन languages में type inference नहीं है, उनमें typing बोझिल हो सकती है
- Java example में
Person person1 = newPerson();जैसी repetition आती है - लेख में बाद में यह correction जोड़ा गया कि Java में type inference है
- Java example में
- Rust जैसी type inference वाली languages में
let person1 = new_person();जैसा code ज्यादा concise होता है - Types के फायदे पाने के लिए language समझने वाली modern code completion capabilities वाला code editor या IDE चाहिए
vimबनामemacs, tabs बनाम spaces जैसे taste debates के उलट, types में cost के मुकाबले benefit बड़ा है, इसलिए उन्हें न इस्तेमाल करने की वजह समझना मुश्किल है—यह stance है- आगे का लेख using the type system effectively है
1 टिप्पणियां
Hacker News की राय
इस चर्चा में सबसे निराशाजनक बात यह है कि सब कुछ इस बारे में है कि लोग कैसे महसूस करते हैं, और अनुभवजन्य प्रमाण कम हैं
मौजूदा शोध के हिसाब से दोनों तरीकों के बीच कोई अर्थपूर्ण अंतर नहीं दिखता, और जब तक कोई नया शोध न हो, यह पक्का कहना मुश्किल है कि किसी की पसंदीदा तरफ ही सही है
निजी तौर पर मुझे typed languages पसंद हैं, लेकिन TypeScript जैसे type systems मुझे अपर्याप्त लगते हैं। runtime पर types को सच में इस्तेमाल नहीं किया जा सकता, इसलिए runtime bugs रह जाते हैं, और बहुत सारी runtime logic को type system में encode नहीं किया जा सकता, इसलिए जो cases impossible होने चाहिए उन्हें अब भी खुद check करना पड़ता है
अगर type system runtime bugs के बारे में सोचने की जरूरत लगभग खत्म कर दे, तो यह जबरदस्त फायदा होगा, लेकिन ज्यादातर languages उस स्तर तक नहीं पहुंचतीं और overhead व कुछ benefits के बीच के धुंधले middle ground में रह जाती हैं
bugs की संख्या या speed में बड़ा फर्क न दिखने की वजह शायद अंततः offset होना है। type safety net न हो तो आप ज्यादा tests लिखते हैं, और उल्टा type system पर जरूरत से ज्यादा भरोसा करें तो आखिर में लगभग उतने ही runtime bugs बच जाते हैं। काश इस विषय पर ठोस research होती, लेकिन यह कठिन समस्या है
असल मुद्दा उससे ज्यादा यह है कि types में invest करने लायक नहीं है, ऐसा मानने के subjective कारण क्या हैं
कुछ साल पहले मैंने developer productivity पर research देखी थी, और लगभग सब या तो बहुत खराब थी या केवल juniors पर ठीक से लागू होती थी। उदाहरण के लिए beginners को static errors पर fast feedback से बड़ा फायदा मिलता है
अच्छी experimental design को college students के बजाय professionals पर लागू करना लगभग असंभव है, और individual differences, development की किस्म, management style जैसे असंख्य variables को अलग करना पड़ता है, इसलिए signal निकालना मुश्किल है। दुख की बात है, जीवन की कई चीजें प्रभावी ढंग से measure करना कठिन होता है
लेख और कई comments programmer सुविधा, productivity, और “correctness” की बात करते हैं, लेकिन मौजूदा research में ऐसा कोई अर्थपूर्ण परिणाम नहीं है कि static typing इन चीजों को सुधारती या बिगाड़ती है। मूलतः यह subjective है
हालांकि static typing का एक वास्तविक effect है जिसे मामूली रूप से prove किया जा सकता है: यह अधिक efficient code लिखने देता है। type discipline की चर्चा में यही केंद्र में होना चाहिए, बाकी बातें इस stage पर काफी हद तक हवा-हवाई हैं
लेख में इस्तेमाल किया गया TypeScript असल में strong typing नहीं है; यह static typing है, लेकिन weak typing है। types annotations जैसे हैं, इसलिए performance या memory layout की guarantee भी नहीं है। इसलिए documentation के अलावा, static typing की cost चुकाने के बावजूद वास्तविक benefits बहुत कम मिलते हैं
यह हैरानी की बात है कि tech community वास्तविक evidence को नजरअंदाज कर cultural और personal preferences को facts की तरह स्वीकार कर लेती है
दूसरी techniques की तरह इसमें भी holes हैं, इसलिए maximum reliability के लिए कई techniques को मिलाना चाहिए। सिर्फ इसलिए static typing छोड़ देना कि यह सब कुछ नहीं पकड़ती, वैसा है जैसे कहना कि चोर खिड़की तोड़ सकता है इसलिए दरवाजा lock नहीं करेंगे। अगर security सच में महत्वपूर्ण है, तो दरवाजा भी lock करना चाहिए और खिड़कियों पर bars भी लगाने चाहिए; दोनों में से सिर्फ एक चुनने की बात नहीं है
अगर language general programs लिखने जितनी powerful है, तो वह bugs बनाने जितनी भी powerful है
static typing कुछ खास तरह के bugs पकड़ने में effective हो सकती है, लेकिन सभी में नहीं। कभी-कभी यह static unit tests या executable documentation के लिए domain-specific language की तरह readability बढ़ाती है
आम तौर पर dynamic languages ज्यादा agile होती हैं और tests को आसानी से व ज्यादा मात्रा में लिखने देती हैं। static typed language होती तो जिन tests की जरूरत नहीं पड़ती, वे भी होते हैं, इसलिए types अब भी useful हैं, लेकिन जितना अक्सर माना जाता है उतनी universally powerful नहीं हैं
static types पसंद करने के social pressure से अलग, आखिरकार static types से दूर जाने की वजह हमेशा यह रही कि उनके आसपास ivory tower खड़ा कर दिया जाता था
दोनों paradigms में मैंने 10-10 साल software बनाया है, और अब मैं type system न इस्तेमाल करने को प्राथमिकता देता हूँ
dynamic types मुझे ऐसी मजबूरी लगती हैं जो सरल code लिखवाती है, जैसे unit tests composable code को मजबूर करते हैं। यानी ऐसा code जो पढ़ने और समझने में आसान हो
यह दावा भी मुझे खास सही नहीं लगता कि novice developers के लिए codebase तक पहुँच आसान हो जाती है। क्योंकि यह बिना समझे सिर्फ लाल निशान हटाने वाले repetitive loop को बढ़ावा दे सकता है। type system हर project में language के ऊपर एक और बहुत domain-specific language सीखने पर मजबूर करता है, और अक्सर वास्तविक behavior समझने में बाधा डालता है
लेख में बताए गए problems को type जितने robust, लेकिन समझने में आसान तरीकों से हल किया जा सकता है। types को simple तरीके से इस्तेमाल किया जा सकता है, लेकिन मेरे अनुभव में असल में ऐसा लगभग कभी नहीं हुआ। मुझे autocomplete भी पसंद नहीं है, तो इसे ऐसे ही समझें
हो सकता है मैं बस “code ही documentation है” चिल्लाने वाला बूढ़ा developer हूँ, लेकिन यह विचार आजकल industry में भरे उन developers के प्रति गहरी नाराज़गी से भी आया हो सकता है जो कहते हैं “ChatGPT ने सही कहा है और salary भी मोटी मिलती है”
लेकिन आम तौर पर उलटे evidence ज़्यादा मजबूत लगते हैं। असली dynamic code को document करने के लिए बाद में बनाए गए types अक्सर उसी feature को शुरू से static types के साथ implement करने की तुलना में कहीं अधिक जटिल होते हैं। TypeScript ecosystem का DefinitelyTyped इसके अनगिनत उदाहरण देता है
यह कहना मुश्किल है कि वे types “सरल तरीके से इस्तेमाल” किए जाते हैं, लेकिन वह complexity type system खुद से या type definitions देने के तरीके से नहीं आती, बल्कि उस dynamic code की complexity से आती है जिसे वे explain करते हैं
शुरू से static types के साथ बने equivalent packages में interfaces आम तौर पर अधिक सरल होते हैं। क्योंकि types को बाद में मौजूदा API में fit नहीं करना पड़ता, बल्कि पहले ही define किया जाता है
मैं तो यहाँ तक मानता हूँ कि interface को explicit किए बिना आप जान ही नहीं सकते कि वह simple है या complex। “code documentation है” वाले ideal से सहमत हूँ, लेकिन अगर interface को explicit करने वाला code नहीं है, तो वह interface definition के हिसाब से under-documented है
सच में ऐसा scene बगल में बैठकर देखना चाहूँगा। मेरे क्षेत्र में domain-specific logic dynamic-typed codebase में लगभग समझ से बाहर होती है, जबकि statically typed code developers को business logic सिखाता है
“code documentation है” वाली बात भी उल्टा भ्रमित करती है। मेरे अनुभव में code documentation तभी बनता है जब static types हों। उनके बिना यह जानने का तरीका नहीं होता कि object में कौन-सी properties हैं, या जिस property के न होने की उम्मीद है उसे check क्यों किया जा रहा है। comments होते तो हैं, लेकिन meaningful comments छोड़ने वाले लोग मैंने बहुत कम देखे हैं
मेरा अनुभव उलटा है। बहुत dynamic patterns पर ठीक से types लगाना मुश्किल होता है, और अच्छा type system सरल patterns को encourage करता है, जिससे types भी सरल हो जाते हैं
लाल निशान हटाना महत्वपूर्ण है। लाल निशान का मतलब है कि कोई problem है, और यह किसी और तरीके से error ढूँढने की तुलना में कहीं आसान है। समझ नहीं आता कि कोई उस error को बाद में क्यों खोजना चाहेगा
autocomplete भी पसंद नहीं है—यह बात मुझे static typing के विरोधियों पर भरोसा न करने वाली तरफ ले जाती है। जो programmer नहीं चाहता कि computer programming में उसकी मदद करे, वह बहुत suspicious लगता है
लेकिन इससे उस technology की technical merits कम नहीं होतीं। कोई technology शानदार हो सकती है और उसके आसपास के लोग दिखावे से भरे हो सकते हैं
dynamic typing सरल code लिखवाती है—यह दावा ऐसा लगता है जैसे “आँखों पर पट्टी बाँधकर गाड़ी चलाओ तो धीरे चलोगे, इसलिए अच्छा है।” अगर लक्ष्य यही है तो line length या parameter count की limit लगाने वाला linter इस्तेमाल किया जा सकता है; constraint को indirect बनाने की जरूरत नहीं
types अकेला समाधान नहीं हैं, लेकिन investment के मुकाबले return बहुत बड़ा है, इसलिए मेरे हिसाब से यह पहले निकाला जाने वाला tool है। investment लगभग नहीं और लाभ बड़ा
“code documentation है” से सहमत हूँ, लेकिन types भी code का हिस्सा हैं। इसलिए मैं कहना चाहूँगा: “code documentation है, और types code का हिस्सा हैं”
हमारा क्षेत्र engineering है, एक ही सही जवाब नहीं होता, और सब कुछ trade-off है। बल्कि इसी वजह से हमारा काम तुरंत automate होकर गायब नहीं हो जाता
इस thread में दूसरे engineers की राय या experience के कारण उन्हें “कमतर समझने” वाला माहौल सच में अप्रिय है
जब अधिकतर data JSON के रूप में network पर आता-जाता है, तब strong static typing लागू करने की लड़ाई अक्सर काफी inconsistent तरीके से लड़ी जाती है
जो tools संभव हों, सब इस्तेमाल करने चाहिए, लेकिन अधिकतर “data” हमारी सोच से कहीं ज़्यादा ढीला-ढाला होता है। लोग phone number को string इसलिए नहीं रखते कि वे आलसी हैं, बल्कि इसलिए कि कभी उन्हें लगा था कि इसे stronger type बनाया जा सकता है, और फिर बहुत बार चोट खाई। नाम, address, postal code के साथ भी यही है
ये values users से लेनी होती हैं, और व्यावहारिक रूप से text parse करने के अलावा कोई रास्ता नहीं होता। अगर आप system ऐसा बनाते हैं कि parsing से पहले original text store न हो, तो किसी दिन लगभग निश्चित रूप से पछताएँगे
मेरी नज़र में सबसे अच्छा तरीका है original input text को सुरक्षित रखते हुए backend users को typed data set के रूप में देने वाली एक layer बनाना, लेकिन हर किसी को अपने छोटे-से domain में देखना होगा कि इतना investment worth it है या नहीं
अगर heavy evaluation करना है, तो SAT या किसी अन्य numeric model में बदलने वाली layer की जरूरत पड़ने की संभावना ज्यादा है। उस दुनिया में numbers abstraction हैं। इसे किसी और तरह से करने की कोशिश करेंगे तो लगभग निश्चित रूप से दर्द होगा। problem को formalization में और solution space को domain में translate करने वाली layer रखना अच्छा है, और types इसमें मदद कर सकते हैं, लेकिन असल में जिस “types” पर ध्यान जाता है, वह अक्सर ऐसे types नहीं होते
Serde और Pydantic जैसी libraries की वजह से हम deserialization ही validation है वाला तरीका अपनाते हैं। JSON data को code के structs में बदलने से पहले सब कुछ validate करते हैं
Redis example जैसा ही है: network से JSON मिले तब भी, पूरी तरह validate होने के बाद जब वह code तक पहुँचता है तो हम भरोसा कर सकते हैं कि वह well-typed है। इसलिए code में हम मान सकते हैं कि email type valid email है, और ID type valid ID है
name field और address field को मिलाना लगभग हमेशा error होता है, और type system इसे enforce कर सकता है
अगर typo runtime error बन जाए, तो वह “तेज़ी से आगे बढ़ना” नहीं है; और जब function signature बदलते समय codebase में grep करके सभी call sites ढूँढने पड़ें और दुआ करनी पड़े कि सब ठीक कर दिए हों, तो वह “ज़्यादा productive” भी नहीं है
Types अच्छे हैं, लेकिन किसी भी चीज़ की अति समस्या बन जाती है। अगर आप हर business logic को type system में encode करना जीवन का लक्ष्य बना लें, तो ऐसा भ्रम पैदा होता है जो types बिल्कुल न होने से भी ज़्यादा समझ से बाहर होता है। अगर error message में type name एक लाइन में न समाए, तो आप बहुत आगे निकल चुके हैं
निष्पक्ष होकर कहूँ तो वे पुराने pure JS code से मेल कराने के लिए थीं, और उस बेचारे variable में हर तरह की values आ सकती थीं
मैं TypeScript का हमेशा आभारी रहूँगा, लेकिन भविष्य में “वह दौर जब types बहुत आगे चले गए थे” शीर्षक वाले किसी paper में ऐसा code छप जाए तो मुझे हैरानी नहीं होगी
from pdb import set_trace: set_trace()से interactive editing हो सकती है, इसलिए compiler की तुलना में running program से interact करना बेहतर हैलेकिन program थोड़ा भी जटिल होते ही स्थिति बदल जाती है। queues से systems के बीच data पास करना, async・threads・multiprocessing इस्तेमाल करना, और performance-critical हिस्सों में compiled binary libraries लगाना शुरू करें, तो अंत में लगता है काश सब कुछ Erlang में लिखा होता
क्या 100% code coverage वाले बेहद strict tests जैसी कोई व्यापक methodology होती है?
अगर build या compile time पर call sites पकड़े नहीं जा रहे हैं, तो आप static types इस्तेमाल कर ही नहीं रहे
Typos सबसे मजबूत statically typed language में भी गलत code बना सकते हैं। वरना code लिखने का मतलब ही क्या होगा? Runtime error और बिना error के गलत result देने में से क्या ज़्यादा बुरा है?
Python project चलाना C++ compile करने से तेज़ हो सकता है, और dynamically typed languages भी function calls खोजने के लिए grep से बेहतर तरीके दे सकती हैं
यह कहना कि types न इस्तेमाल करने से development speed तेज़ होती है, मेरे अनुभव में सही नहीं है। Static types रोज़मर्रा की programming को तेज़ बनाते हैं
IDE static types की वजह से बेहतर होता है—यह बात आगे आई है—लेकिन REPL में भी यह महसूस होता है। Statically पकड़े गए type errors असली root cause के कहीं ज़्यादा करीब meaningful error messages देते हैं, और runtime errors की तुलना में जल्दी ठीक हो जाते हैं
Types के बारे में बहुत सतर्क होकर सोचने का बोझ भी कम हो जाता है। Compiler discipline बनाए रखता है, इसलिए मुझे कम चिंता करनी पड़ती है। यह भरोसा कि errors की एक बड़ी category तुरंत पकड़ी जाएगी, मुझे तेज़ी से आगे बढ़ने देता है
मेरे अनुभव में static type systems इस्तेमाल में आसान होते हैं, development तेज़ करते हैं और reliability बढ़ाते हैं। अब तक लागत सिर्फ दो रही है: सीखना कठिन हो सकता है, और implement करना कठिन होता है
6 महीने बाद hire होने वाला junior developer typeless code में ढलने में कहीं ज़्यादा धीमा होगा
मैं मानता हूँ कि कुछ लोगों के लिए पहली बार लिखना “तेज़” हो सकता है, लेकिन उसके बाद उस code को पढ़ने वाला हर developer धीमा हो जाता है
सिर्फ compiler पास करने वाला उलझा हुआ code नहीं, बल्कि यह सोचना कि ठीक-ठीक क्या अंदर जा रहा है और क्या बाहर आ रहा है, और क्यों—अच्छी बात हो सकती है
मुझे लगता है लेखक लगभग हर बिंदु पर गलत है। मैं भी दशकों तक ऐसा ही सोचता था, लेकिन पिछले कुछ वर्षों में मेरी सोच पूरी तरह बदल गई
क्या types bugs घटाते हैं? नहीं। बहुत थोड़ा हो सकता है, पर meaningful नहीं। संबंधित research देख लें
क्या types बेहतर development experience देते हैं? नहीं। मेरे REPL और IDE में सभी definitions और variables हैं। सभी symbols की autocomplete, call tree, usage navigation, confident refactoring, functions को अकेले चलाना・बदलना・wrap करना—सब REPL और application के अंदर कर सकता हूँ
क्या सब कुछ type system में encode किया जा सकता है? असंभव। Runtime validation चाहिए
Requirements बदलने पर type definitions खोलकर सुलझाने के लिए भी शुभकामनाएँ। यही निर्णायक बात है। Static types आपके मौजूदा समझे हुए domain data model को बहुत जल्दी freeze कर देते हैं। वह model बदलेगा, और बदकिस्मती हो तो उसी runtime के अंदर कई domain model variants support करने पड़ेंगे। खासकर inheritance इस्तेमाल किया हो तो और भी परेशानी होगी
यह सही है कि static types compiler optimization के लिए बड़ा leverage देते हैं, लेकिन dynamic typed languages में भी कुछ ऐसी हैं जो static types को optional add-on के रूप में देती हैं
कई use cases में, खासकर enterprise development में, dynamic typing के साथ immutability-first functional language लंबे समय में बड़ा फायदा देती है
Strong typing के समर्थक अक्सर जो category error करते हैं, वह यह मानना है कि वही code बस types के बिना लिखा जाएगा। असल में लोग ऐसे नहीं लिखते
लगभग हर बिंदु पर मेरा निष्कर्ष इसका ठीक उलटा है। Runtime validation चाहिए—यह बात निश्चित रूप से सही है, लेकिन अधिकांश runtime validation से बचा जा सकता है
Requirements बदलने की बात करें तो मुझे लगता है static types उल्टे adaptation आसान बनाते हैं। जिन dynamic type systems का मैंने अनुभव किया, उनमें data structures के बारे में महत्वपूर्ण assumptions इधर-उधर बिखरे थे, और कभी वे pre/postconditions के रूप में dynamically check होते थे, कभी सिर्फ tests में होते थे, या कभी बिल्कुल check नहीं होते थे
Requirement बदलने के लिए इन implicit assumptions पर पड़ने वाले सभी impacts का अनुमान लगाना पड़ता था, इसलिए बदलाव डरावना लगता था। नए code से app चालू करना आसान है, लेकिन यह जानना बहुत कठिन है कि कहीं कोई अनसोचा rare code path टूट तो नहीं गया
मुझे static analysis step कहीं ज़्यादा पसंद है जो बताए, “आपने यह interface बदला है; क्या आपको पता है कि यह code path उस हिस्से पर निर्भर था?” Static types ही एकमात्र तरीका नहीं हैं, लेकिन उसी स्तर की dynamic validation और tests रखने की तुलना में यह बहुत कम बोझ लगता है
Type system runtime validation को खत्म नहीं करता, लेकिन सही इस्तेमाल करने पर उसे dramatically घटा देता है
Requirements बदलने पर सबसे अच्छी बात यह है कि compiler ठीक-ठीक बता देता है कि फिर से चलने के लिए क्या ठीक करना होगा। Dynamic language में वही काम करें तो खुद trace करना पड़ता है, unit tests fail होने का इंतज़ार करना पड़ता है, और दुआ करनी पड़ती है कि कोई path छूट न गया हो
किसी खास type का जहाँ-जहाँ इस्तेमाल हुआ है, उसे बहुत अधिक confidence के साथ खोज सकता था और देख सकता था कि हर जगह बदलाव चाहिए या नहीं। Dynamic typed environment में यह काम कहीं ज़्यादा बारीक और झंझट भरा था
C++, Python, JS में लाखों लाइनें लिख चुके डेवलपर के तौर पर भी मुझे ठीक से नहीं पता। यह इतना साफ़ नहीं है
इन तीनों में उत्पादकता रहती है, लेकिन Python आम तौर पर जीतता है। हालांकि गेम इंजन या वीडियो codec Python में नहीं लिखूंगा
JavaScript असंगत और अजीब है, लेकिन Netscape की विरासत ने हम सबको पहले ही उससे बांध दिया है
बहुत object-oriented और विशाल nested classes वाली style में, compile/parse समय पर static types कई गलतियों को कम कर सकते हैं। लेकिन मुझे लगने लगा है कि object-oriented programming कुल मिलाकर लगभग आपदा जैसी है, और सरल functions व structured data लगभग हमेशा simplicity और maintainability में जीतते हैं
आधुनिक language servers और IDE, JS/Python development के दौरान भी कई typing errors पकड़ सकते हैं। Programming के कई हिस्सों को लेकर मैं काफ़ी सख्त रहता हूं, लेकिन static type बनाम dynamic type पर मेरी कोई मजबूत राय नहीं रही। दोनों के पास लाखों सफल projects हैं
मेरे हिसाब से किसी भाषा द्वारा दिया जा सकने वाला सबसे बड़ा productivity boost garbage collection है। मुझे यह जानने में ज़्यादा दिलचस्पी है कि कहीं ज्यादा सरल syntax और types वाला Go इसकी तुलना में कैसा रहेगा। Java भी बेहतर हो सकता है। भले ही verbose हो, ज़्यादातर कामों में cognitive load C++ के मुकाबले बहुत कम होता है
मैं typos भी बहुत करता हूं और arguments का order भी अक्सर गलत कर देता हूं। खासकर machine learning करते समय types बड़ी मदद करते हैं। Data processing में 30 मिनट लगाने के बाद training code का crash होना उन चीज़ों में से है जिनसे मैं सबसे ज़्यादा बचना चाहूंगा
Python की gradual typing मुझे fast prototyping और, function के पर्याप्त परिपक्व हो जाने पर type annotations जोड़ने के तरीके के बीच एक बहुत अच्छा middle ground लगती है
यह बहस तो निपट चुकी है कि strong typing, weak typing से बेहतर है, लेकिन static typing dynamic typing से बेहतर है या नहीं, यह अभी तय नहीं हुआ है
static typing के समर्थक मानते हैं कि compiler को type invariants verify करके “correctness” की पुष्टि करनी चाहिए, और dynamic typing के समर्थक इसे समय की बर्बादी मानते हैं
मैं साफ तौर पर दूसरी तरफ हूं। क्योंकि compiler program correctness नहीं, सिर्फ type correctness जांच सकता है। type correctness program correctness के लिए जरूरी है, लेकिन पर्याप्त नहीं। static typing के समर्थक इस बात को स्वीकार नहीं कर पाते, और यह भ्रम पालते हैं कि static typing असलियत से ज्यादा guarantee देती है
लेख के
birthdayGreetingवाले उदाहरण को देखें। लेखक खुश है किbirthdayGreeting("John", "20")में"20"number नहीं है, इसलिए static typing bug पकड़ लेती है। लेकिनbirthdayGreeting(" ", 123)नहीं पकड़ा जाता।" "कोई नाम नहीं है।birthdayGreeting("Anna," -12335)भी नहीं पकड़ा जाता। उल्टेbirthdayGreeting("Anna" 4.5)पकड़ा जाता है, जबकि 4.5 को भी उम्र माना जा सकता है, इसलिए यह बल्कि गलत कहा जा सकता हैयह महत्वपूर्ण है। “type bugs” पकड़ना मामूली रूप से आसान है, लेकिन semantic bugs सालों तक छिपे रह सकते हैं। जैसे
uintमें store किए गए account balance का overflow, किसी खास जगह पर prime होना चाहिए लेकिन न होने वाला number, ऐसी list जो खाली नहीं होनी चाहिए, आदि। dependent types भी ऐसे invariants की guarantee नहीं दे पातेभरोसा न हो तो उन बड़े bugs को खोज लें जिनसे spacecraft विस्फोट या car accidents हुए। मेरी जानकारी में असली type error वजह रहा हो, ऐसा कोई मामला नहीं था; भारी बहुमत semantic errors का था
[1] ज्यादातर लोग यह नहीं समझते कि types को कम-से-कम दो axes—strong/weak और static/dynamic—पर देखना चाहिए, और weak typing व dynamic typing को लगातार मिला देते हैं। C static और weakly typed है, Python strong और dynamically typed है, और JavaScript weak और dynamically typed है
इसी वजह से मैं static typing के पक्ष में हूं। यह इतना मामूली है कि इसे declaratively, जांचे जा रहे code के ठीक पास, तत्काल feedback के साथ, हर call site और हर sub-expression/statement पर संभाला जा सकता है
type annotations का मतलब यह नहीं कि semantics या domain logic सही है; उसे अब भी test करना होगा। लेकिन यह उन दर्जनों मामूली tests की जगह ले सकता है जो आपकी रुचि वाली logic से orthogonal होते हैं। सच कहूं तो ऐसे tests पूरी तरह लिखने वाले लोग बहुत कम होते हैं
लेख के बाद वाले हिस्से में मैंने कहा था कि user input जैसी जगहों पर types बनाते समय validation होता है। इसलिए
Nametype हमेशा valid है और" "नाम नहीं है। type valid name की guarantee देता है, इसलिए हमारे codebase में यह निश्चित रूप से पकड़ा जाएगाbirthdayGreeting("Anna" 4.5)औरbirthdayGreeting("Anna," -12335)JS में इसलिए असल में valid हैं क्योंकिnumberfloating-point होता है। हालांकि लेख लिखते समय मेरे मन में integer था। यह एक और मामला है जहां TS से ज्यादा strict type, जैसे Rust, invariants को बेहतर define करने में मदद करता हैसंक्षेप में, simple example दिखाने की कोशिश में मैंने हमेशा की तरह सभी types को सख्ती से define नहीं किया, और नतीजतन वे bugs और सामने आए जिन्हें types से पकड़ा जा सकता था
Nametype की है जो हमेशा valid name दर्शाए, और एकAgetype की जो हमेशा valid age दर्शाएvalidation उन types के constructor की एक जगह पर रखी जाए, और
birthdayGreetingजैसी कई methods उस type के values को बिना जिम्मेदारी लिए इस्तेमाल कर सकेंtype checking या कम-से-कम optional type hints और static analysis के बिना इस pattern को अच्छी तरह implement करने का तरीका मुझे नहीं पता। इसके बजाय हर method में input values validate करना बहुत बोझिल है, और यह मान लेना कि caller valid values देगा और फिर बड़े हादसे न हों इसके लिए test करना भी संतोषजनक नहीं है
birthdayGreetingको 1~150 range accept कराने जैसा काम ADA में आसानी से किया जा सकता हैspacecraft से जुड़े public issues में कुछ ऐसे भी हैं जिन्हें बेहतर type checking शायद पकड़ सकती थी। metric/imperial conversion में भी units को type में डाल देना चाहिए। हालांकि [2] के मामले में integration tests की गलती होने की संभावना ज्यादा है
बेशक type checking code की सभी समस्याएं, खासकर algorithmic समस्याएं, नहीं खोज सकती और tests की जगह भी नहीं लेती। फिर भी development phase में immediate feedback और type hints बेहद कीमती हैं
example में name string को person type या object में भी बदला जा सकता है
[1]: https://en.m.wikipedia.org/wiki/Ariane_flight_V88
[2]: https://en.m.wikipedia.org/wiki/Mars_Climate_Orbiter
कोई perfect solution नहीं है, लेकिन valuable solutions बहुत हैं
इस समस्या पर काफी convergence हो चुका है। अब ज़्यादातर भाषाएँ statement level पर किसी न किसी हद तक type inference देती हैं। C++ में भी
autoहैइसकी वजह से code में type boilerplate काफी कम हो गया है। C++
forstatement में लंबे iterator type पूरे लिखने वाले दिन अब बीत चुके हैंFunction declarations और struct fields वे जगहें हैं जहाँ code पढ़ने के लिए type information चाहिए होती है। Program कुछ सौ lines से आगे जाए या developers एक से ज़्यादा हों, तो किसी न किसी स्तर की annotation ज़रूरी होती है
मुख्य विरोध स्वाभाविक रूप से Python और JavaScript users से आता है। Python ने बाद में एक बहुत अजीब सलाह-जैसा type system जोड़ा, और JavaScript ने बाद में TypeScript जोड़ा। दोनों जोड़े गए type systems हैं, और ऐसे माहौल में इस्तेमाल होते हैं जहाँ typed और untyped code मिला-जुला होता है। यह दर्दनाक है
LISP ने भी दशकों पहले “flavors” और Common LISP Object System के साथ बाद में type system जोड़ा था, और वह भी देखने में अच्छा नहीं था। सीख यह है कि type system को बाद में जोड़ने पर गड़बड़ होती है
OptionalकाNoneइस्तेमाल से पहले check enforce करना, याtyping.Protocolके जरिए structural subtyping जैसी अच्छी सुविधाएँ हैं। अगर Python शुरू से types को ध्यान में रखकर design किया गया होता तो बेहतर होता, लेकिन मौजूदा Python code के साथ integrate करने और कोई भी code न तोड़ने की जरूरत को देखते हुए यह काफी अच्छा किया गया हैPython static typing की बड़ी समस्या ecosystem और conventions हैं। यह खास तौर पर इसलिए और खराब हुई है क्योंकि कई developers असल में data scientists की तरह Python इस्तेमाल करते हैं। ठीक-ठाक method signatures लिखने में आलस के कारण
*args/**kwargsका दुरुपयोग करते हैंMethods का DataFrame या dictionaries को कबाड़ के थैले की तरह पास करना बहुत आम है। अगर method columns या fields जोड़ता-हटाता है, ताकि code चलाने या हर line पढ़ने तक यह पता न चले कि data bag में क्या है, तो bonus points हैं
बेशक लगभग हर भाषा में ऐसा ही किया जा सकता है। C# में हर type को
dynamicके रूप में इस्तेमाल करना या Go methods से सब कुछinterface{}लेना भी संभव है। लेकिन Python ने लंबे समय तक इस approach को सक्रिय रूप से बढ़ावा दिया, और आज भी कई beginner tutorials “*kwargsलेने पर function signature बदलने की जरूरत नहीं पड़ती” को किसी भयानक trap की बजाय smart लोगों के लिए advanced feature की तरह पेश करते हैंGradual migration के लिए यह अनिवार्य है, और इनका इस तरह काम करना पूरी तरह समझ में आता है
Flavors उस Lisp में लाया गया था जिसमें type system नहीं था, और बाद में CLOS को Common Lisp में जोड़ा गया, जो पहले से type system वाला Lisp था
लोग हमेशा reason से ज़्यादा उन चीज़ों को लेकर कट्टर रहे हैं जिनसे वे emotionally चिपके होते हैं
“types bugs घटाते हैं” वाला दावा सच होने से ज़्यादा plausible लगने वाला दावा है
https://blog.metaobject.com/2014/06/the-safyness-of-static-t...
ऐसा भी नहीं कि कोशिशें कम हुई हों। कहा जा सकता है कि यह दावा खंडित हो चुका है
हालांकि व्यक्तिगत रूप से मुझे static types पसंद हैं। मुख्य रूप से उनके documentation effect की वजह से, और संयोग हो या नहीं, सच में मजबूत empirical evidence वाला positive effect भी सिर्फ उसी तरफ है
शुरुआत में सही code लिखने से ज़्यादा regressions रोकना अहम हो सकता है, और जो code evolve नहीं होता उसे देखते समय इस हिस्से का आकलन नहीं हो पाता
खास तौर पर, किसी बड़े pure JavaScript project में object field हटाना मूल रूप से minefield है और अतीत में इससे कई bugs हुए हैं। वहीं पूरी तरह TypeScript project में वही बदलाव आत्मविश्वास के साथ किया जा सकता है
मुझे लगता है इसे इस बात का empirical evidence माना जा सकता है कि बहुत strong static types bugs घटाते हैं
आखिरकार, कई comments की तरह, types कोई binary yes/no मामला नहीं हैं, बल्कि static/dynamic, strong/weak आदि कई axes वाला बड़ा spectrum हैं। Type systems के बीच भी बहुत अंतर है, और लोग उन type systems को problem पर कैसे apply करते हैं, उसमें भी बड़ा अंतर है
Static और strong typed language में भी हर चीज़ को string से represent करके लगातार convert किया जा सकता है, जो असल में dynamic typed language की तरह काम करना है। उलट, अगर type system के tools का इस्तेमाल करके valid values को represent करने वाली classes बनाई जाएँ और important invariants assert किए जाएँ, तो फायदा मिल सकता है
Productivity कई गुना नहीं, बल्कि कई orders of magnitude बढ़ जाती है। यह बात मैं C, C++, Java, Python, JavaScript, TCL जैसी इस spectrum के बड़े हिस्से को cover करने वाली कई भाषाओं का खूब इस्तेमाल करने के अनुभव से कह रहा हूँ
हाल में न छुए गए code को, चाहे वह current project हो या dependency, reason करना बहुत आसान हो जाता है। किसी function द्वारा return किए गए object से ठीक-ठीक क्या किया जा सकता है यह जानने के लिए लगातार side tracks पर नहीं जाना पड़ता, इसलिए सामने की problem पर ज़्यादा focus कर सकते हैं
Compile pass होने पर जो अच्छी राहत महसूस होती है, वह भी है, लेकिन वह secondary है
Emotional attachment सिर्फ “लेकिन इससे सारे bugs तो खत्म नहीं होते!” का सामना करने पर आने वाले गुस्से में दिखती है
यह debate बिल्कुल वैसी ही लगती है
मुझे बस इतनी straightforward rationality चाहिए कि जब मैं किसी hashmap को Apple या String की तरह treat करने की कोशिश करूँ, तो compiler कहे “नहीं”