`if` को ऊपर ले जाएँ, `for` को नीचे धकेलें
(matklad.github.io)- दो code structuring rules मुख्य हैं: condition branching को caller की ओर ऊपर खींचना, और repeated processing को batch operations की ओर नीचे धकेलना
ifको ऊपर ले जाने से preconditions को type या assert से enforce करना आसान हो जाता है, और जटिल control flow को एक जगह इकट्ठा करके duplicate conditions और dead branches ढूँढना आसान होता हैenumबनाने के तुरंत बाद फिर सेmatchकरने वाली संरचना एक ही condition को branching, data structure, और re-branching में दोहराती है, इसलिए इसेfoo(x)औरbar(y)कॉल तक सरल बनाने की गुंजाइश होती हैforको नीचे ले जाने पर individual object iteration के बजाय batch operations मूल इकाई बन जाते हैं, जिससे startup cost amortization, processing order बदलना, vectorization, और struct-of-array optimization संभव हो जाते हैं- condition को loop के बाहर निकालने से hot loop में branching कम की जा सकती है, और यह TigerBeetle जैसी उस संरचना तक भी जुड़ता है जहाँ control plane के decision cost को data plane की batch processing पर amortize किया जाता है
if को ऊपर ले जाना
- किसी function के अंदर का
ifcondition पहले इस नज़र से देखना चाहिए कि क्या उसे caller की ओर ले जाया जा सकता है- उदाहरण के तौर पर,
Option<Walrus>लेकरNoneहोने पर return कर देने वाले function की तुलना में सीधेWalrusलेने वाला function बेहतर रूप माना गया है - precondition को function के अंदर जाँचकर “कुछ न करना” से बेहतर है कि caller उसे जाँचे और type या assert से उसके सही होने को enforce करे
- उदाहरण के तौर पर,
- precondition checks को ऊपर ले जाने वाला refactoring call path के साथ propagate हो सकता है, और नतीजतन पूरे सिस्टम में checks की संख्या कम हो सकती है
- control flow और
ifजटिलता बढ़ाते हैं और अक्सर bugs का कारण बनते हैंifको ऊपर ले जाने से जटिल branching logic एक function में इकट्ठा हो जाती है, और असली काम सीधे-सादे lower-level routines में चला जाता है- जटिल control flow पूरे file में फैला हो, उससे बेहतर है कि वह एक screen में दिखने वाले एक function में हो, क्योंकि तब duplicate और dead conditions ढूँढना आसान होता है
- “dissolving enum” refactoring उस pattern को हटाने का तरीका है जिसमें branch को पहले data structure में बदला जाता है और फिर दोबारा branch किया जाता है
- अगर
f()condition के हिसाब सेE::Foo(x)याE::Bar(y)बनाता है, औरg(e)फिरmatchसेfoo(x)याbar(y)को call करता है, तो वही condition कई रूपों में दोहराई जाती है - condition को
main()तक ऊपर ले जाने पर इसेif condition { foo(x) } else { bar(y) }जैसे सरल रूप में बदला जा सकता है
- अगर
for को नीचे ले जाना
- data-oriented नज़रिए से program और hot path अक्सर बहुत सारे objects को संभालते हैं, इसलिए objects के batch को introduce किया जाता है और batch operations को default case माना जाता है
- individual object के लिए scalar version, batch operation का special case बन जाता है
for walrus in walruses { frobnicate(walrus) }की तुलना मेंfrobnicate_batch(walruses)बेहतर रूप है
- इसका मुख्य फायदा performance है
- पूरे batch को एक साथ handle करने पर startup cost को amortize किया जा सकता है
- processing order को लचीले ढंग से बदला जा सकता है, और vectorization या struct-of-array जैसे तरीके भी संभव हो जाते हैं जहाँ पहले सभी entities के एक field को process किया जाए
- एक चरम उदाहरण के रूप में Vectorized Interpreters Talk का लिंक दिया गया है
- एक दिलचस्प उदाहरण के तौर पर FFT-आधारित polynomial multiplication आता है
- कई points पर polynomial को एक साथ evaluate करना, individual points पर बार-बार evaluate करने से तेज़ हो सकता है
ifऔरforवाले नियम साथ में भी लागू किए जा सकते हैं- अच्छी संरचना वह है जहाँ
if conditionके बाहर branch के हिसाब सेfor walrus in walrusesचलाया जाए - खराब संरचना वह है जहाँ
forloop के अंदर हर बारif conditionevaluate किया जाए - इससे condition के दोबारा evaluation से बचा जा सकता है, hot loop से branching हटाई जा सकती है, और vectorization संभव हो सकती है
- अच्छी संरचना वह है जहाँ
- यह pattern micro level और macro level दोनों पर लागू होता है
- TigerBeetle की architecture data plane में object batches को एक साथ process करके control plane के decision cost को amortize करती है
forसंबंधी सलाह का मुख्य motivation performance है, लेकिन jQuery की तरह collection unit पर काम करने का तरीका expressiveness में भी मदद कर सकता है
1 टिप्पणियां
Hacker News की रायें
पहले मुझे आश्चर्य हुआ कि इस लेख को इतना विरोध क्यों मिल रहा है, लेकिन जब याद आया कि यह डेटा-ओरिएंटेड डिज़ाइन वाली सलाह है, तो बात समझ आई
इस फ़ोरम के कई लोगों की तरह मैं भी ज़्यादातर काम के लिए web apps बनाता हूँ, और उस संदर्भ में यह सलाह बकवास जैसी लग सकती है
अगर रोज़मर्रा के काम में आपको instruction cache की चिंता करने की ज़रूरत नहीं पड़ती, तो यह heuristic सही लगता है कि इस सलाह को मोटे तौर पर नज़रअंदाज़ किया जा सकता है
अगर यह अंदाज़ा लगाना हो कि यह सलाह कब महत्वपूर्ण हो जाती है, तो Mike Acton की “Typical C++ Bullshit” खोजकर देखना अच्छा होगा, और यह लेख उसी बात को ज़्यादा समझने लायक ढंग से व्यवस्थित करता हुआ लगता है
Casey Muratori की चिंता से मैं काफ़ी सहमत हूँ, लेकिन अधिकतर business software को प्रदर्शन से ज़्यादा बदलाव-योग्यता और correctness, यानी “समय के साथ programming” के लिए optimize करना चाहिए
डेवलपर जटिल business logic को class के अंदर छोटे-छोटे private methods में बाँटकर DRY बनाना चाहते हैं, और “if को ऊपर उठाओ” branching logic को कई methods में बिखरने से रोकता है
“for को नीचे ले जाओ” भी महत्वपूर्ण है। कई call flows अंत में महंगे database queries पर खत्म होते हैं, और अक्सर ऊपर का loop नीचे कई बार DB calls बनाता है
उस repetition को अक्सर SQL के
whereclause याjoinसे बदला जा सकता है, और aggregation या filtering को भी ढेर सारे objects निकालकर loop करने के बजाय DAO के करीब धकेलना DB के पास optimize करने के लिए बेहतर होता हैहालांकि हर design principle की तरह इसे सिद्धांतवादी ढंग से नहीं, बल्कि सोच-समझकर लागू करना चाहिए
बदलाव-योग्यता और performance को एक-दूसरे के विरोध में देखना अफ़सोसजनक है। असल में दोनों के टकराने का कोई ठोस सबूत मैंने अभी तक नहीं देखा
performance के सबसे महत्वपूर्ण पहलुओं में से एक है चीज़ों को छोटा बनाना। छोटा code, छोटे data structures, और executed instructions की कम संख्या ही मूल बात है, और “instruction cache के बारे में सोचना” भी मूल रूप से छोटा code लिखना ही है
code जितना छोटा होगा, bugs घुसने की जगह उतनी कम होगी, उसे एक बार में समझना आसान होगा, और test coverage पाना भी बेहतर होगा, इसलिए यह correctness के पक्ष में है। बदलाव-योग्यता के लिए भी code जितना छोटा होगा, बदलाव उतना छोटा होगा
बेशक parallelization, caching, low-level optimization जैसे optimizations code को जटिल बना सकते हैं, लेकिन ये performance optimization का सिर्फ़ एक हिस्सा हैं, और performance को गंभीरता से लेने वाला programmer profiling और analysis के बिना ऐसे काम नहीं करता
if/forऔरfor/iffunctionality के लिहाज़ से बहुत अलग न हों, फिर भी एक तेज़ हो सकता है; इसलिए अगर बिना लागत के सही वाला इस्तेमाल किया जा सकता है, तो धीमा विकल्प चुनने की वजह नहीं हैसरल programs में call stack छोटा होता है और वे जटिल व महंगे abstractions से बचते हैं, इसलिए वे कम memory इस्तेमाल करते हैं और तेज़ चलने की प्रवृत्ति रखते हैं
सर्वोच्च स्तर की performance के पीछे जाने पर program जटिल हो सकता है, लेकिन असली simplicity ज़्यादातर reasonable performance भी देती है
“for को नीचे ले जाना” मुख्यतः data-oriented सलाह है, लेकिन “if को ऊपर उठाना” program को ज़्यादा सरल बनाने के करीब है। ठीक-ठीक कहें तो यह source code की locality बढ़ाने की बात है, और branching logic को एक जगह इकट्ठा करना इसका मूल है: https://loup-vaillant.fr/articles/source-of-readability
खासकर “loop को नीचे ले जाओ” CRUD apps में शक्तिशाली है। जहाँ तक हो सके creation और updates को batch processing में करने से अक्सर CPU bottleneck से कहीं ज़्यादा समय बचता है
items.map(insertToDb/postToServer)औरinsertToDb/postToServer(items)के बीच का अंतर लगभग हर मामले में कई orders of magnitude का होता हैमैंने देखा है कि ऐसे optimization से काम का समय seconds/minutes से milliseconds तक घट जाता है, और अक्सर API भी ज़्यादा साफ़ हो जाती है और logs पढ़ना भी आसान हो जाता है
अनुभव बढ़ने के साथ मुझे लगता है कि बहुत ज़्यादा programmers छोटी-छोटी इकाइयों के “सुंदर code” पर तो ध्यान देते हैं, लेकिन पूरे codebase design पर पर्याप्त ध्यान नहीं देते
संक्षिप्त और अच्छी तरह तराशे हुए functions अच्छी बात हैं, लेकिन ऐसे लेख PR या discussions में बेकार के bikeshedding में बदल सकते हैं
function थोड़ा messy है या नहीं,
ifऔरforकहाँ रखे गए हैं,mapऔरfilterइस्तेमाल हुए हैं या नहीं—अगर function का नाम ठीक है, interfaces और types expressive हैं, उद्देश्य साफ है, documentation है, और side effects का जरूरत से ज़्यादा इस्तेमाल नहीं है, तो ये बातें मुझे बहुत मायने नहीं रखतींअगर सोचें कि input मिलने पर validation कहाँ करना है, तो इस heuristic के हिसाब से input लेने वाली सबसे ऊपरी जगह पर validate करना चाहिए
यह code समझने में भी मदद करता है, और proof के नजरिए से भी preconditions को ऊपर propagate करना चाहिए, इसलिए तकनीकी रूप से भी जरूरी है
पहली सलाह निश्चित रूप से कोई मामूली बहस नहीं है, और दूसरी थोड़ी ज्यादा धुंधली है
वे juniors को अपने ही पैर पर कुल्हाड़ी मारने से बचाना चाहते हैं, लेकिन complexity की cost और code लंबे समय में कैसे बदलता है, इसकी समझ अक्सर अभी पर्याप्त नहीं होती
उल्टा, 20 साल से ज्यादा करने के बाद ज्यादातर technical तत्वों की तुलना में simplicity को ज्यादा महत्व देने लगते हैं
“branch कितनी जल्दी करें”, “इस code को असल में करना क्या है” जैसे सवाल लंबे समय में सबसे मूल्यवान जवाब देते हैं
Abstraction और encapsulation से जुड़े सवाल उल्टे ऊपर बताए गए तरह की बहसों की ओर आसानी से ले जाते हैं, और “big picture” ही देखने वालों के code में security issues सबसे ज्यादा इसलिए भी निकलते हैं क्योंकि वे वास्तव में उस well-designed codebase के काम को ठीक से नहीं समझते
Data-oriented design के लिए अच्छी तरह फिट होने वाले performance-sensitive software में ऐसी छोटी इकाइयों वाली चीजों पर ज्यादा ध्यान देना चाहिए। Compiler optimization इसी तरह काम करती है
तब game के rules बदल जाते हैं। Statements का अर्थ महत्वपूर्ण हो जाता है, self-explanatory code ज्यादा मायने रखने लगता है, code comments सिर्फ reasoning के लिए रह जाते हैं, और “documentation” specification और user manual के ज्यादा करीब हो जाती है
ज्यादा से ज्यादा आप functions को group कर सकते हैं, और उसके बाद सब खुद संभालना पड़ता है
“if को ऊपर ले जाना” का नुकसान यह है कि function definition के अंदर preconditions और postconditions सीधे दिखाई नहीं देते, और हर call site पर check करना पड़ता है
कई लोगों वाले बड़े projects में ऐसा function अपने intended context के बाहर reuse होकर bugs पैदा कर सकता है
Contract framework इस्तेमाल करें तो हल हो सकता है, लेकिन condition को contract और code में दो बार लिखना पड़ता है, और dependent types के साथ भी यही है
किसी खास context से जुड़े code region को tag करना, और उस context के अंदर ही call किए जा सकने वाले functions define करना एक दिलचस्प तरीका है
Python में
@requires_context("VALIDATED_XY")जैसे decorator औरvalidated_xycontext manager से function को सिर्फ validated region में call करवाया जा सकता हैRuntime उस context का meaning नहीं जानता, लेकिन tools और tests के साथ इसे इस तरह design किया जा सकता है कि desired context सिर्फ तब बने जब कोई खास condition satisfy हो
Haskell जैसी languages में identity monad जैसी चीज से type level पर enforce किया जा सकता है, और type-level enforcement न भी हो तो भी “unsafe” code regions को protect करने का यह दिलचस्प तरीका हो सकता है
इसलिए precondition अभी भी function definition में सीधे दिखती है, बस
ifstatement नहीं बल्कि type signature के हिस्से के रूप मेंयह लेख में इस्तेमाल Rust में common pattern है, और
ifcheck के उलट runtime नहीं बल्कि compile time पर check होने वाली strict precondition है; satisfy न होने पर program compile ही नहीं होताजैसे
do_something(position: ValidatedPosition)के रूप में लेना, और सामान्यPositionको validate करने के बादValidatedPositionमें बदलकर pass करनाअसल में validation को
ValidatedPositionconstructor के अंदर encapsulate करेंगे, लेकिन मुख्य बात यह है कि अगरPositionको सीधे pass करने की कोशिश करेंगे तो mypy बताएगा कि गलत type pass किया गया हैPython की type checking Rust जितनी comprehensive नहीं है, लेकिन जब यह guarantee चाहिए कि pass किया गया data ठीक से process हुआ है, तब यह धीरे-धीरे उपयोगी होता जा रहा है
अगर किसी function की precondition है, तो स्वाभाविक रूप से function की शुरुआत में assert किया जा सकता है। उदाहरण के लिए Java type system null allow करता है, इसलिए जिस function को object चाहिए वह null आने पर exception throw करे
हर call site की जिम्मेदारी है कि precondition true होने पर ही function call करे। precondition की परिभाषा से यह स्वाभाविक है
Precondition तोड़कर function call करना caller का bug है। Function के अंदर इसे check करके undefined behavior रोकने वाला code जरूरी हो सकता है, लेकिन ऐसे assertions और program के actual control flow में फर्क करना चाहिए, और लेख बाद वाली बात पर है
fn frobnicate(walrus: Walrus)में, ownedWalrusन होने वाली चीज pass करें तो program compile नहीं होगाGeneric होने पर भी argument को trait bounds satisfy करने पड़ते हैं, और function के अंदर argument कैसे use किया जाता है, उसके आधार पर compiler function definition के लिए जरूरी bounds मांगता है
publicऔरprivateका intention कुछ हद तक code को किसी खास context के हिसाब से tag करने का ही रहा होगाया फिर
public,private, और .NET ecosystem केprotectedजिन क्षेत्रों को handle करते हैं, उनसे पार जाने वाली ज्यादा specific semantics की जरूरत हो सकती हैपहला उदाहरण
ifऔरforकी वजह से नहीं, बल्कि किसी और वजह से अच्छा नहीं हैआम तौर पर अगर किसी चीज़ का “container” है, तो function उस container पर नहीं, बल्कि उसके अंदर मौजूद domain-level “object” पर लिखना बेहतर होता है
Clojure में agent इस्तेमाल करते समय agent के लिए functions नहीं लिखते, बल्कि उस object के लिए functions लिखते हैं जिसे agent hold कर सकता है
Elixir में भी core domain functions PID पर नहीं, बल्कि अंदर की domain data structure पर काम करते हैं, और ज़रूरत पड़ने पर GenServer call उसी को delegate कर दी जाती है
इससे ज़्यादा flexibility मिलती है, और core domain concern “Walrus को frobnicate करना” तथा application concern “Walrus हो भी सकता है और नहीं भी; अगर है तो frobnicate करना” को ज़्यादा साफ़ तरीके से अलग किया जा सकता है
शुरुआत में validation करना अच्छा है, लेकिन यह भी ज़रूरी है कि function अजीब error के साथ अचानक फटने के बजाय साफ़ तौर पर validation error दे
Haskell इस समस्या को newtype से हल करता है, यानी “एक transparent container जो certify करता है कि proper validation पहले ही हो चुकी है”
जो सलाह मैं सच में लोगों पर ज़ोर देकर कहना चाहूँगा, वह है “sad if” को प्राथमिकता दें। normal path को कई layers में nest करने की तुलना में, गलत conditions को ऊपर से एक-एक करके check करना और तुरंत ठीक करना या stop करना लगभग हमेशा ज़्यादा readable और maintainable होता है
इंसान स्वभाव से expected case पर focus करना चाहता है, लेकिन code इसके उलट exceptional cases पर पहले focus चाहता है। हर
ifदिमाग़ पर बोझ बनाता है, और अगर external system से information लानी हो या error के कारण early exit करना हो, तो detection और handling साथ-साथ होने पर वह बोझ तुरंत उतर जाता हैwalrus.frobnicate()होता तो बेहतर होता, तो मुझे लगता है यह लेखक के core point से ज़्यादा syntax preference का मामला हैमैं यह नहीं कहूँगा कि यह खराब सलाह है, लेकिन यह ज़रूरी नहीं कि अच्छी सलाह भी हो
चुनी गई language Rust है, यह बात काफी कुछ कहती है। strong type system दूसरी languages में ज़रूरी बहुत सारी defensive programming को रोक देता है
अगर कोई C programmer function में pass किए गए pointer की validity check न करके NULL dereference कर देता है, तो मैं ऐसे व्यक्ति के साथ team में काम नहीं करना चाहूँगा
इसलिए कम से कम कुछ
ifनिश्चित रूप से नीचे की तरफ होने चाहिए, और अच्छा हो कि errors ठीक से ऊपर propagate होंforके बारे में मेरी राय इतनी strong नहीं है, लेकिन C में array argument pointer में decay हो जाता है, इसलिए मुझे लगता है कि iteration भी नीचे नहीं बल्कि ऊपर होनी चाहिए। जिस function में array बनाया गया है, वहाँ length पर भरोसा किया जा सकता है, लेकिन जिस function को argument के रूप में pass किया गया है, वहाँ नहींdocumentation को strong type system की जगह लेनी चाहिए, runtime checks को नहीं
NULL checks और अन्य defensive measures से भरा code कहीं ज़्यादा पढ़ने में कठिन होता है
library boundary पर ज़्यादा defensive checks की ज़रूरत कही जा सकती है, और यह लेख ठीक ऐसे ही checks को ऊपर धकेलने की बात करता है
security-critical code अलग हो सकता है, लेकिन ज़्यादातर मामलों में accidental NULL dereference tests, sanitizers और fuzzing से पकड़ा जाए तो काफी है
invalid pointer value check करने के लिए आपको संभावित अनगिनत गलत values का पूरा set check करना होगा, और अगर आप सिर्फ NULL check करते हैं तो असल में invalid value check नहीं कर रहे होते
अगर function की precondition है “parameter
pNULL नहीं हो सकता”, तो check किया जा सकता है। लेकिन अगर precondition है “pvalid pointer होना चाहिए”, तो सही assertion condition ढूँढने के लिए शुभकामनाएँआजकल मैं T-SQL काफी इस्तेमाल करता हूँ, और parameters या variables को
NOT NULLdeclare नहीं किया जा सकताइसलिए जितना reasonably possible हो उतना early, आम तौर पर stored procedure के बिल्कुल ऊपर, NULL check करना अच्छा है
वरना NULL call hierarchy में गहराई तक अनपेक्षित रूप से propagate होकर कम स्पष्ट problems पैदा कर सकता है
अच्छी बात है कि table data को
NOT NULLdeclare किया जा सकता है, इसलिए ऐसे bugs आम तौर पर data खराब नहीं करते, लेकिन उन्हें जल्दी पकड़ना कहीं ज़्यादा आसान हैहालांकि अगर किसी parameter value के आधार पर DB में लिखने वाली logic हो और वह value अनपेक्षित रूप से NULL हो, तो गलत value लिखी जा सकती है या ज़रूरी value बिल्कुल नहीं लिखी जा सकती, जिससे असल में data corrupt हो सकता है
इसलिए defensive programming ही जवाब है
उचित context के बिना देखें तो यह सलाह काफी अजीब है, और शायद खराब सलाह भी हो सकती है
loops और
ifstatements दोनों ही control flow operations हैं, इसलिए लेख के कुछ arguments बहुत समझ में नहीं आतेसबसे मजबूत argument performance लगता है, लेकिन performance आम तौर पर सबसे आखिर में चिंता करने वाली चीज़ होनी चाहिए, खासकर जब बात heuristic advice की हो
अफ़सोस है कि लेखक ने इसे catchphrase बना दिया, और मैं चाहूँगा कि यह न फैले
लेखक के example की तरह अगर improved code लिखा जा सकता है, तो condition loop execution के दौरान constant है। अगर हर बार कोई महंगी condition evaluate नहीं हो रही, तो branch prediction ज़्यादातर काम संभाल लेगा
अगर condition const values से बना boolean expression है, तो compiler के भी इसे समझ लेने की संभावना है
मुझे लगा था कि पूरा लेख ऐसे examples तक जाएगा: condition के आधार पर
walrusको बाँटकरfrobnicate_batch(fwalrus)औरtransmogrify_batch(twalrus)call करने का तरीकालेकिन असल में वह एक condition से पूरी loop को दो branches में wrap करने वाले example पर चला गया
software engineering background से आए programmers को यह अक्सर गलत करते देखना हैरान करता है
मैंने scientific field में programming शुरू की थी, और वहाँ ऐसी चीज़ों के बारे में सोचना बिल्कुल आवश्यक है
loop order गलत रखने भर से simulation 1 घंटे की जगह 1 हफ्ता ले सकता है
उस background की वजह से
forऔरifका order सही रखकर छोटी optimizations करना instinctive हो गया है, और जो code ऐसा नहीं करता वह बस गलत लगता हैमुझे यकीन नहीं है कि ऐसे सामान्य नियम असल code पर सच में लागू किए जा सकते हैं या नहीं
ऐसे नियम अक्सर गलत जगह रखे गए सिद्धांत जैसे लगते हैं, और भले ही कोई blog post खुद को heuristic कहकर शुरू करे, युवा programmers उसे हमेशा उसी तरह नहीं लेते
कुछ हफ्ते पहले YouTube मुझे “I'm a never-nester” नाम का video लगातार recommend कर रहा था, जो शायद यह दावा कर रहा था कि
ifको कभी nest नहीं करना चाहिए, और यह काफी हास्यास्पद थालेख के उदाहरण की तरह
forके अंदरif conditionवाले code को “बुरा” बताया गया था, लेकिन असल में इस तरह लिखे गए code के ज़्यादातर मामलों मेंconditionwalrusपर निर्भर होती है, इसलिएifको ऊपर नहीं ले जाया जा सकताअगर ऊपर ले जाना संभव हो, तो वही condition हर loop में दोबारा evaluate करना इतना साफ़ तौर पर बेकार दिखता है कि programmers स्वाभाविक रूप से इससे बचते हैं
लेकिन अगर कोई junior या student किसी सिद्धांत जैसा सुनाई देने वाला सुझाव पढ़ता है, तो वह उसे सख्ती से मानने की कोशिश में और खराब code बना सकता है
match (condition_a, condition_b)से चार cases अलग करना nestedifसे बेहतर न हो—ऐसा मैंने अनुभव में कभी नहीं देखाifको “ऊपर ले जाने” का अच्छा उदाहरण लगता हैcode का मकसद condition के आधार पर walrus पर कोई खास operation करना है, लेकिन सच में
ifpolymorphism और type system की जगह इस्तेमाल हो रहा हैwalrus में अलग-अलग scenarios में call किए जाने वाले दो functions क्यों होने चाहिए? एक function और दो types रखकर सही type pass क्यों न किया जाए?
मौजूदा structure में भी ऊपर की तरफ condition के आधार पर processing function चुना जा सकता है, और नीचे वाले loop में चुने हुए function को हर बार call करवाया जा सकता है
फैसला जितना जल्दी हो सके कर लिया जाए, तो उसे पूरे code में बिखेरने की जरूरत नहीं रहती। code का अंदरूनी हिस्सा बिना branch के हर बार वही काम करता है, और output सिर्फ configuration graph के जरिए बदलता है
बेशक यह नया विचार नहीं है; 15 साल पहले भी यह पहले से पुराना idea था: https://www.youtube.com/watch?v=4F72VULWFvc
walrusपर निर्भर न हो, और यह बात स्पष्ट रूप से दिखाने में मदद करती हैयहां “for को नीचे ले जाओ” फिर से लागू करने पर
if conditionके अंदरfrobnicate_batch(walruses)याtransmogrify_batch(walruses)call करने जैसा रूप बनता हैजिस समस्या से मैं कई बार टकराया था लेकिन ठीक से बयान नहीं कर पाया था, उसे किसी ने शब्दों में व्यवस्थित कर दिया, और यह दिमाग में रखने लायक अच्छा model लगता है
साथ ही ऐसी आलोचना भी मूल्यवान है। उम्मीद है कि कोई dogmatic junior developer इसे साथ में पढ़े और थोड़ा ज्यादा सूक्ष्म तरीके से निर्णय ले
programmers को खासकर ऐसी चीजों को copy करके यांत्रिक रूप से लागू नहीं करना चाहिए
heuristics और best practices को आंख मूंदकर लागू करना अच्छा विचार नहीं हो सकता, और आखिरकार सब कुछ “संदर्भ पर निर्भर” होता है
ऐसे heuristics में आम तौर पर ज्ञान की थोड़ी-सी धूल होती है, लेकिन ज्यादातर वे नई समस्या पैदा करते हैं जिसे नए programmers से फिर से de-dogmatize करना पड़ता है
जब कोई उन्हें बहुत सख्ती से मानने की कोशिश करता है तो चीजें खराब होने के मामले हमेशा बहुत होते हैं, और “यह जानो कि इस सलाह को कब नहीं मानना है” ही असल में मुख्य कठिनाई है
मुझे लगता है ये rules आज़माने के लिए उपयोगी हैं। इन्हें अपनाकर देखो, पूरी सीमा तक धकेलकर देखो, एक दिन या एक साल बाद उलटकर भी देखो, और देखो कि ये कहां तक जाते हैं
सीमाएं सीखना भी लगातार फिर से लिखे जा रहे parchment पर जुड़ने वाली एक और परत है
उन koans में से कुछ को एक-दूसरे से विरोधाभासी होना चाहिए