4 पॉइंट द्वारा GN⁺ 2025-08-20 | 1 टिप्पणियां | WhatsApp पर शेयर करें
  • बाएँ से दाएँ प्रोग्रामिंग करने का तरीका यह सुनिश्चित करता है कि कोड टाइप करते ही प्रोग्राम वैध स्थिति में बना रहे, जिससे editor autocomplete जैसे tool support का अधिकतम लाभ मिलता है
  • Python के list comprehension में घोषित न किए गए variables और type inference की अनुपस्थिति autocomplete को बाधित करती है
  • Rust और JavaScript में प्रोग्राम को स्वाभाविक रूप से बाएँ से दाएँ बनाया जा सकता है, जिससे variable usage और method discovery अधिक सहज हो जाती है
  • C और Python की functional style में function names या structure की discoverability की कमी efficient coding experience को कमजोर करती है
  • उच्च जटिलता वाले logic में बाएँ से दाएँ unfold होने वाला code अधिक पढ़ने योग्य होता है, और maintenance व extensibility में बेहतर होता है

बाएँ से दाएँ प्रोग्रामिंग करना

कोड टाइप करते ही वैध होना चाहिए


Python list comprehension की सीमाएँ

  • Python का list comprehension syntax words_on_lines = [line.split() for line in text.splitlines()] ऐसी समस्या पैदा करता है जिसमें undeclared variable (line) तक पहुँचना पड़ता है, इसलिए editor autocomplete या type inference ठीक से उपलब्ध नहीं करा पाता
  • कोड को आंशिक रूप से टाइप करने की प्रक्रिया में
    • अगर words_on_lines = [line.sp तक टाइप किया जाए, तो editor line का type नहीं जानता, इसलिए methods सुझा नहीं सकता
    • variable name की typo (lime आदि) जैसी संभावित errors को पकड़ना भी कठिन हो जाता है
  • सही suggestions पाने के लिए incomplete code लिखना पड़ता है, और यह प्रक्रिया गैर-सहज और असुविधाजनक हो जाती है

Rust में बाएँ से दाएँ निर्माण

  • Rust का उदाहरण let words_on_lines = text.lines().map(|line| line.split_whitespace()); यह दिखाता है कि
    • anonymous function की declaration के साथ variable (line) पहली बार आते ही declared माना जाता है, इसलिए तुरंत autocomplete और method suggestions संभव हो जाते हैं
    • वास्तव में split_whitespace method भी autocomplete की मदद से आसानी से मिल गया
  • यह तरीका प्रोग्राम को हमेशा कम से कम आंशिक रूप से वैध स्थिति में रखता है, इसलिए IDE या editor real time में coding support दे सकते हैं

Progressive Disclosure और API usability

  • Progressive Disclosure एक design principle है जिसमें user केवल उतनी ही complexity देखता है जितनी उसे चाहिए, और इसे programming पर भी लागू किया जा सकता है
    • उदाहरण: word processor में image जोड़ने पर ही संबंधित options दिखना, यह उसी तरह का UX है
  • C language में ऐसा support कमज़ोर है
    • FILE *file से जुड़े functions को file. के ज़रिए explore नहीं किया जा सकता, इसलिए fread, fclose जैसे function name patterns याद रखने पड़ते हैं और capabilities खोज पाना कठिन होता है
    • इसके विपरीत, किसी आदर्श language में file. के माध्यम से method suggestions के जरिए संबंधित functionality को आसानी से क्रमिक रूप से खोजा जा सकता है

Functions और methods की discoverability का अंतर

  • Python के map(len, text.split()) और JavaScript के text.split(" ").map(word => word.length) उदाहरणों की तुलना
    • Python में len, length, size जैसे function names का अनुमान लगाना आसान नहीं होता, इसलिए कई बार कोशिश करनी पड़ती है कि वास्तव में क्या काम करेगा
    • JavaScript में word. के बाद सिर्फ .l टाइप करने पर भी editor length जैसी methods सुझा देता है, इसलिए discoverability अधिक होती है
    • map जैसे higher-order functions में भी actual return value और data type तुरंत अधिक स्पष्ट दिखाई देते हैं

Logic जितना जटिल, structured writing के फायदे उतने अधिक

  • अधिक जटिल logic (जैसे filter, lambda वाले nested लंबे Python code) में
    • code की शुरुआत और अंत बार-बार देखना पड़ता है, और condition expressions या parentheses matching जैसी चीज़ों में readability घटती है और समझना कठिन हो जाता है
  • उसी logic के JavaScript version में code को ऊपर से नीचे, बाएँ से दाएँ क्रमवार पढ़ा और समझा जा सकता है

मुख्य सिद्धांत

कोड टाइप करने के हर क्षण वैध होना चाहिए

  • सिर्फ text टाइप करने पर भी प्रोग्राम वैध स्थिति में रहता है
  • text.split(" ") तक लिखने पर भी, और फिर आगे .map(word => word.length) जोड़ते समय भी, पूरा code हमेशा intermediate state में वैध रहता है
  • ऐसा coding pattern editor के real-time support की संभावना बढ़ाता है, और REPL environment में तुरंत परिणाम देखना भी संभव बनाता है

निष्कर्ष

  • API और language design को ऐसा समर्थन देना चाहिए कि code को बाएँ से दाएँ स्वाभाविक रूप से टाइप करते हुए हर intermediate stage पर वैध प्रोग्राम बनाया जा सके
  • अच्छा API design इस coding experience को बेहतर बनाने की कुंजी है

1 टिप्पणियां

 
GN⁺ 2025-08-20
Hacker News राय
  • SQL की एक कमी यह है कि query statement FROM की बजाय SELECT से शुरू होती है, इसलिए यह तुरंत समझना मुश्किल होता है कि किस entity (table) पर काम हो रहा है, और smart editor के लिए query लिखने में ज़्यादा प्रभावी मदद करना भी कठिन हो जाता है, FROM -> SELECT -> WHERE क्रम ज़्यादा स्वाभाविक लगता है, खासकर क्योंकि SELECT clause में column names तय किए जाते हैं और WHERE में उन्हें refer किया जाता है, इसलिए और भी ऐसा लगता है, वास्तव में SELECT * FROM table की जगह सिर्फ FROM table लिखने पर भी SELECT clause छोड़ा जा सकता है, मुझे पता है ऐसी शिकायत से मैं किसी बड़बोले बूढ़े की तरह लग सकता हूँ, लेकिन यह बस मेरी निजी कसक है
    • PSQL और PRQL वास्तव में FROM-first query order का इस्तेमाल करते हैं, BigQuery में भी हाल ही में pipe/arrow syntax जोड़ा गया है, और DuckDB community extension भी हैं, इसलिए इन्हें recommend करता हूँ DuckDB - PSQL, DuckDB - PRQL
    • SQL ऐसा इसलिए लिखा जाता है क्योंकि relational algebra की बुनियाद में projection को हमेशा पहले लिखा जाता है, इसलिए standard के अनुसार WHERE में column alias नहीं इस्तेमाल किए जा सकते, क्योंकि selection(WHERE) projection(SELECT) से पहले होती है, वैसे MySQL 8 में TABLE <table> नाम का syntax भी है, देखना दिलचस्प हो सकता है
    • वास्तव में ज़्यादातर SQL engine का internal processing order FROM -> WHERE -> SELECT होता है, इसलिए SELECT में define किए गए column alias GROUP BY, HAVING, ORDER BY में तो इस्तेमाल होते हैं, लेकिन WHERE में नहीं
    • C# में SQL में compile होने वाला DSL(LINQ-to-SQL) भी FROM-first structure रखता है, और IDE में दूसरे clauses लिखते समय autocomplete की वजह से field suggestions तुरंत मिल जाती हैं, इसलिए यह structure मुझे अच्छा लगता है
    • Kusto, जो Azure की data analysis query language है, भी pipe का उपयोग करने वाला ऐसा ही रूप है Kusto query परिचय, .NET का LINQ style भी ऐसा ही है, ईमानदारी से कहूँ तो SQL में भी FROM से शुरू होने वाले variants को थोड़ा और सक्रिय रूप से लाया जाना चाहिए, और मुझे नहीं लगता कि यह कोई मुश्किल काम है, usability सुधारने की कोशिश कम दिखती है
  • मुझे समझना मुश्किल लगता है कि Python इतना पसंद क्यों किया जाता है, जैसे ही दो या अधिक लोग साथ काम करते हैं, यह भाषा अंतहीन पीड़ा बन जाती है, लेखक ने जिन बातों की ओर इशारा किया है, वे तो बस हिमशैल की नोक हैं
    • मुझे लगता है यह वैसा ही है जैसे लोग Lisp-परिवार की भाषाओं की ओर नहीं जाते, mathematical rigor का मतलब readability नहीं होता, Python की list/dict/set comprehensions भी type तय करने वाले for loop जैसी ही हैं, सब लोग Python के loose typing को लेकर चिंतित रहते हैं, लेकिन return type को साफ़ करने वाला एकमात्र syntax(list comprehension) ही निशाने पर आता है, यह अजीब है, Rust समेत ज़्यादातर दूसरी भाषाओं में भी क्रम from iter as var नहीं है, और अलग-अलग भाषाओं ke function call syntax की तुलना करना भी मज़ेदार है(Python में भी functools.map है)
    • सिर्फ इसलिए कि कोई चीज़ समझ में नहीं आती, वह अपने-आप गुण नहीं बन जाती, Python के पसंद किए जाने के पीछे निश्चित ही कुछ कारण हैं, हाँ, कमियाँ भी साफ़ हैं, लेकिन सिर्फ वही मायने नहीं रखता, फायदे और नुकसान को कुल मिलाकर देखना चाहिए, और दूसरी भाषाओं के साथ भी ऐसे ही तुलना करनी चाहिए
    • मुझे भी Python पसंद है(लेकिन छोटे teams और छोटे, कम उम्र वाले programs के लिए), static typing न होने से implementation तेज़ होती है, लेकिन मज़बूत type system की मदद से सब कुछ पूरी तरह बिखरता भी नहीं, शायद इसी वजह से यह data science में लोकप्रिय है, exploration के दौरान यह बहुत सुविधाजनक है, दूसरी तरफ़ लंबे समय तक कई teams द्वारा maintain किए जाने वाले या बड़े programs में इसकी स्पष्ट कमियाँ हैं, आखिर कोई एक universal language नहीं होती, कम-से-कम एक “जल्दी कोशिश करके तेज़ी से बढ़ने वाली भाषा(Soft)” और एक “लंबे समय तक maintain करने के लिए अच्छी भाषा(Hard)” तो चाहिए ही
    • मैं भी पहले इस राय से पूरी तरह सहमत था, लेकिन type annotations और type checking की वजह से दूसरों के लिखे Python code के साथ सहयोग करना बहुत आसान हो गया है, अब भी मुझे नहीं लगता कि यह बहुत बड़े projects तक जाता है, लेकिन types जुड़ने के बाद Python मेरी सबसे पसंदीदा scripting language बन गई है
    • मैं भी shared codebase में list comprehension जैसी चीज़ों से बचता हूँ और बहुत simple Python style पसंद करता हूँ, इसे “एक ही तरीका होना चाहिए” वाली भाषा कहा जाता है, लेकिन असल में इसमें बहुत ज़्यादा तरीके साथ मौजूद हैं, list comprehension मुझे निजी तौर पर मज़ेदार और संतोषजनक लगती है, लेकिन अगर सबको एक ही रास्ते पर चलना है, तो यह syntax नहीं होना चाहिए
  • “program टाइप करते ही valid होना चाहिए” इस दावे से सहमति है, लेकिन हक़ीक़त में हम code हमेशा बाएँ से दाएँ, line-by-line क्रम में नहीं लिखते, कई बार बीच का कोई दूसरा हिस्सा पहले लिखते हैं या variable declaration बाद में करते हैं, उदाहरण के लिए कभी variable पहले इस्तेमाल कर लेते हैं और काफ़ी बाद में declare करते हैं
    • क्योंकि code एक बार लिखा जाता है और दर्जनों, सैकड़ों बार पढ़ा जाता है, इसलिए जो code क्रम से पढ़ा जा सके वह jump माँगने वाले code से कहीं ज़्यादा आसान लगता है
    • सच कहूँ तो यह चर्चा लेख के मुख्य बिंदु से थोड़ा हटती है, लेकिन नज़रिया दिलचस्प है
    • पूरी तरह सहमत, नया file बनाते समय ही code शुरू से क्रम में लिखता हूँ, field जोड़ते समय class definition से शुरू करने के बजाय सीधे उस field का उपयोग करने वाला code पहले लिखता हूँ, condition सुधारते समय भी कुछ देर के लिए code invalid(error वाला) हो जाना आम बात है
    • इस राय से भी सहमत हूँ, लेकिन इससे जुड़ा एक अहम सिद्धांत यह है कि “तुमने अभी coding पूरी नहीं की, इसलिए compile ही नहीं करने देंगे” जैसी structure बहुत ज़्यादा सख़्त है, errors non-blocking होने चाहिए, लेकिन कुछ भाषाएँ अधूरा code ही रोक देती हैं(जैसे unused variable, missing return आदि)
    • कई बार थोड़ा असहज लगता है जब IDE को यह समझ ही नहीं आता कि मैं वास्तव में code किस क्रम में लिख रहा हूँ
  • कुछ IDEs में code template feature होता है, जहाँ आप abbreviation टाइप करते हैं और वह code structure में expand हो जाता है, फिर tab के ज़रिए अलग-अलग placeholders भरते जाते हैं, इस दौरान tab movement का क्रम ज़रूरी नहीं कि बाएँ से दाएँ ही हो, जैसे {3} for {2} in {1} जैसा क्रम भी संभव है, ऐसे tools “पढ़ने में अच्छी syntax” और “टाइप करने में आसान syntax” के बीच समझौता देते हैं, मैं tooling का सहारा लेकर भी readability को प्राथमिकता देने के पक्ष में हूँ, मुझे नहीं लगता कि सिर्फ for-in structure पर अड़े रहना चाहिए
  • आजकल Hacker News पर जैसे एक तरह की सहमति बन गई है कि Python ने pipe operator को छोड़कर गलती की, मैं Mathematica से R में गया तो pipe की क़ीमत जल्दी समझ में आ गई, data science में step-by-step data transformation code लिखते समय यह सचमुच intuitive और readable है, Python कई contexts में इस्तेमाल होती है, लेकिन data analysis के बाहर भी pipe के फायदे कितने हैं, यह जानने की उत्सुकता है, मैं समझना चाहता हूँ कि Python ने pipe क्यों नहीं अपनाया
    • pipe operator से एक कदम आगे बढ़कर reverse assignment भी दिलचस्प हो सकता है, let foo = ... की तरह result को variable में assign करने के बजाय ... =: foo जैसा form भी आज़माना चाहूँगा
    • R(खासकर tidyverse R) का pipe operator मेरे लिए सबसे महत्वपूर्ण “killer app” है, data के साथ काम करने के लिए इतनी आसान और आनंददायक भाषा शायद ही कोई हो, उदाहरण के लिए cookie recipe को bake(divide(add(knead(mix(flour, water, sugar, butter)),eggs),12),450,12) की तरह nested लिखने के बजाय, mix(flour, water, sugar, butter) %>% knead() %>% add(eggs) %>% divide(12) %>% bake(temp=450, minutes=12) की तरह pipe के साथ लिखना बहुत आसान और साफ़ दिखता है
    • Python pandas में pipe syntax इस्तेमाल करें तो यह ऐसे दिखता है
      result = (df
       .pipe(fun1, arg1=1)
       .pipe(fun2, arg2=2)
      )
      
      R में
      result <- df |>
       fun1(., arg1=1) |>
       fun2(., arg2=2)
      
      दोनों पढ़ने लायक हैं, लेकिन R की बढ़त यह है कि pipe dataframe के बाहर भी ज़्यादा अच्छी तरह काम करता है
  • यह बहस FP(functional) vs OOP(object-oriented) language बहस, vim vs emacs बहस जैसी लगभग धार्मिक लड़ाई है, vim में operator पहले आता है, emacs में selection order पहले आता है, अंग्रेज़ी की तरह “English-style पढ़ी जाने वाली” भाषाओं में आम तौर पर verb पहले आता है(Lisp/Scheme ऐसा ही है), जबकि जर्मन, तमिल जैसी भाषाएँ जहाँ verb आख़िर में आता है, वे OOP style(पहले noun) के साथ ज़्यादा मेल खाती हैं, उदाहरण के लिए तमिल में क्रम “water drink” है जबकि अंग्रेज़ी में “drink water” है, इसलिए कुछ लोगों को vim ज़्यादा सहज लग सकता है, यह कहना मुश्किल है कि कौन-सा style बेहतर है; कई बार tools/लोगों की प्रवृत्ति के हिसाब से चीज़ें बनती हैं, और आजकल language model के साथ तो बहुत कुछ संभव है
    • “अगर अंग्रेज़ी की तरह पढ़ने लायक बनाना है, तो verb पहले होना चाहिए?” इस पर, imperative language में हाँ, लेकिन declarative language में अंग्रेज़ी जैसा बनाने का मतलब subject पहले भी हो सकता है
    • “क्या जर्मन में verb हमेशा अंत में आता है?” इस पर, वास्तव में simple sentences में verb दूसरे स्थान पर आता है(I drink waterIch trinke Wasser), यह हमेशा पूरी तरह sentence-end पर नहीं होता
    • vim में operator पहले होने की बात पर, वास्तव में Kakoune इसका उल्टा करता है, और मुझे यह तरीका कहीं अधिक logical लगता है Kakoune के बारे में विवरण
  • दूसरी ओर, Python का from some_library import child_module syntax बहुत intuitive है, JS में import { asYetUnknownModule } from SomeLibrary जैसी structure है, इसलिए वह काफ़ी कम intuitive लगती है
    • JS में namespace import के साथ इसे ऐसे लिखा जा सकता है
      import * as someLibrary from "some-library"
      someLibrary.someFunction()
      
      और वास्तव में IDE autocomplete अच्छी तरह काम करती है, इसलिए यह अच्छा लगता है MDN namespace import विवरण
    • मुझे समझ नहीं आता लोग from keyword पर इतने अटके क्यों रहते हैं, बस
      import SomeLibrary {
        asYetUnknownModule
      }
      
      ऐसा भी तो किया जा सकता है
  • ReScript ने इसी वजह से API को data-last से data-first में बदला, इसकी शानदार type inference की वजह से लगभग हमेशा accurate और type-correct autocomplete मिलती है, इसलिए developer experience बहुत अच्छा हो जाता है, हाँ, अगर बिना reference के function declare करें(तो type पता नहीं होती) तो दिक्कत अब भी रहती है, लेकिन type जोड़कर या पहले call करके इसे सुलझाया जा सकता है, इस विषय पर यह blog post भी बढ़िया है Data-first aur data-last tulna
  • मैं लंबे समय से यह नज़रिया रखता आया हूँ, और यह शायद इस बात से भी जुड़ता है कि Ruby मुझे हमेशा कहीं ज़्यादा आसान क्यों लगी, खासकर क्योंकि मैंने न Python और न Ruby को production स्तर पर बहुत गहराई से इस्तेमाल किया है, फिर भी मुझे समझ नहीं आता कि Python इतना व्यापक रूप से install और इस्तेमाल क्यों होने लगा, Ruby में भी कमियाँ हैं, लेकिन scripting करते समय Python जैसे जटिल बदलावों का सामना शायद कम लोग करते हैं, कम-से-कम Ruby में पिछले 10 वर्षों में कोई बड़ा version-conflict drama तो नहीं हुआ
  • कुल मिलाकर, लेख में उठाए गए बिंदुओं से मैं पूरी तरह सहमत हूँ, मुझे लगता है कि context पहले आए और structure बाएँ से दाएँ पढ़ी जाए तो वह LLM और autocomplete दोनों के लिए बेहतर होगी, हालांकि example code को len(list(filter(lambda line: all([abs(x) >= 1 and abs(x) <= 3 for x in line]) and (all([x > 0 for x in line]) or all([x < 0 for x in line])), diffs))) की तरह लिखने के बजाय NumPy array का उपयोग करना बेहतर है, इससे memory में नई list बनाने की ज़रूरत नहीं पड़ती और पूरी line को एक बार में handle करना भी आसान होता है, उदाहरण के लिए
    sum(1 for line in diffs
      if ((np.abs(line) >= 1) & (np.abs(line) <= 3)).all()
        and ((line > 0).all() or (line < 0).all()))
    
    यह रूप कहीं ज़्यादा “left to right” महसूस होता है
    • numpy version अब भी थोड़ा cryptic है(line > 0 तो ठीक है, लेकिन broadcasting rules जटिल हो सकते हैं), लेखक के JavaScript example या C#, Java, Scala जैसी type-strict भाषाओं की collection APIs ज़्यादा साफ़ लगती हैं, मेरी पसंद Kotlin है, क्योंकि वहाँ इसे ऐसे लिखा जा सकता है
      diffs.countIf { line -> 
        line.all { abs(it) in 1..3 } and ( 
          line.all { it > 0} or
          line.all { it < 0}
        )
      }